diff --git a/llarp/contact/client_contact.cpp b/llarp/contact/client_contact.cpp index 116d0a26f4..81a1d8ea1c 100644 --- a/llarp/contact/client_contact.cpp +++ b/llarp/contact/client_contact.cpp @@ -141,12 +141,11 @@ namespace llarp oxenc::bt_dict_producer btdp; enc.bt_encode(btdp); - auto view = btdp.view_for_signing(); - - if (not crypto::sign(enc.sig, derived_privatekey, view.data(), view.size())) - throw std::runtime_error{"Failed to sign EncryptedClientContact payload!"}; - - btdp.append("~", enc.sig.to_view()); + btdp.append_signature("~", [&](ustring_view to_sign) { + if (not crypto::sign(enc.sig, derived_privatekey, to_sign.data(), to_sign.size())) + throw std::runtime_error{"Failed to sign EncryptedClientContact payload!"}; + return enc.sig.to_view(); + }); enc._bt_payload = std::move(btdp).str(); } diff --git a/llarp/handlers/session.cpp b/llarp/handlers/session.cpp index 66380c3ecb..29c06354a4 100644 --- a/llarp/handlers/session.cpp +++ b/llarp/handlers/session.cpp @@ -334,7 +334,7 @@ namespace llarp::handlers auto enc = client_contact.encrypt_and_sign(); if (publish_client_contact(enc)) - log::debug(logcat, "Successfully republished updated EncryptedClientContact!"); + log::info(logcat, "Successfully republished updated EncryptedClientContact!"); else log::warning(logcat, "Failed to republish updated EncryptedClientContact!"); } @@ -414,7 +414,7 @@ namespace llarp::handlers { log::debug(logcat, "Publishing ClientContact to pivot {}", path->pivot_rid()); - ret += path->publish_client_contact(ecc, true); + ret &= path->publish_client_contact(ecc, true); } } diff --git a/llarp/link/link_manager.cpp b/llarp/link/link_manager.cpp index 73d6a23572..8152bf4b8c 100644 --- a/llarp/link/link_manager.cpp +++ b/llarp/link/link_manager.cpp @@ -706,69 +706,6 @@ namespace llarp log::warning(logcat, "NodeDB query for {} random RCs for connection returned none", num_conns); } - void LinkManager::handle_path_data_message(bstring message) - { - ustring nonce, hop_id_str, payload; - - try - { - oxenc::bt_dict_consumer btdc{message}; - std::tie(hop_id_str, nonce, payload) = ONION::deserialize_hop(btdc); - } - catch (const std::exception& e) - { - log::warning(logcat, "Exception: {}", e.what()); - return; - } - - auto symmnonce = SymmNonce{nonce.data()}; - HopID hopid{hop_id_str.data()}; - auto hop = _router.path_context()->get_transit_hop(hopid); - - if (not hop) - return; - - symmnonce = crypto::onion(payload.data(), payload.size(), hop->shared, symmnonce, hop->nonceXOR); - - // if terminal hop, pass to the correct path expecting to receive this message - if (hop->terminal_hop) - { - NetworkAddress sender; - bstring data; - - try - { - oxenc::bt_dict_consumer btdc{payload}; - std::tie(sender, data) = PATH::DATA::deserialize(btdc); - - if (auto session = _router.session_endpoint()->get_session(sender)) - { - session->recv_path_data_message(std::move(data)); - } - else - { - log::warning(logcat, "Could not find session (remote:{}) to relay path data message!", sender); - } - } - catch (const std::exception& e) - { - log::warning(logcat, "Exception: {}", e.what()); - } - } - else - { - // if not terminal hop, relay datagram onwards - auto hop_is_rx = hop->rxid() == hopid; - - const auto& next_id = hop_is_rx ? hop->txid() : hop->rxid(); - const auto& next_router = hop_is_rx ? hop->upstream() : hop->downstream(); - - std::string new_payload = ONION::serialize_hop(next_id.to_view(), symmnonce, payload); - - send_data_message(next_router, std::move(new_payload)); - } - } - void LinkManager::gossip_rc(const RouterID& last_sender, const RemoteRC& rc) { _router.loop()->call([this, last_sender, rc]() { @@ -1153,8 +1090,13 @@ namespace llarp "publish_cc", PublishClientContact::serialize(std::move(enc), relay_order, is_relayed), [respond = std::move(respond)](oxen::quic::message m) mutable { - if (m.timed_out) - return; // drop; requester will already have timed out + if (m) + log::info(logcat, "Relayed PublishClientContact returned successful! Relaying response..."); + else if (m.timed_out) + log::info(logcat, "Relayed PublishClientContact timed out! Relaying response..."); + else + log::info(logcat, "Relayed PublishClientContact failed! Relaying response..."); + respond(m.body_str()); }); } @@ -1350,81 +1292,154 @@ namespace llarp void LinkManager::handle_path_control(oxen::quic::message m, const RouterID& /* from */) { - ustring nonce, hop_id_str, payload; + HopID hop_id; + std::string payload; + SymmNonce nonce; try { - oxenc::bt_dict_consumer btdc{m.body()}; - std::tie(hop_id_str, nonce, payload) = ONION::deserialize_hop(btdc); + std::tie(hop_id, nonce, payload) = ONION::deserialize_hop(oxenc::bt_dict_consumer{m.body()}); } catch (const std::exception& e) { log::warning(logcat, "Exception: {}", e.what()); - return; + return m.respond(messages::ERROR_RESPONSE, true); } - auto symmnonce = SymmNonce{nonce.data()}; - HopID hopid{hop_id_str.data()}; - auto hop = _router.path_context()->get_transit_hop(hopid); + auto hop = _router.path_context()->get_transit_hop(hop_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; + { + log::warning(logcat, "Received path control with unknown next hop (ID: {})", hop_id); + return m.respond(messages::ERROR_RESPONSE, true); + } - symmnonce = crypto::onion(payload.data(), payload.size(), hop->shared, symmnonce, hop->nonceXOR); + nonce = crypto::onion( + reinterpret_cast(payload.data()), payload.size(), hop->shared, nonce, hop->nonceXOR); // if terminal hop, payload should contain a request (e.g. "ons_resolve"); handle and respond. if (hop->terminal_hop) { - handle_inner_request( - std::move(m), - std::string{reinterpret_cast(payload.data()), payload.size()}, - std::move(hop)); - return; + log::debug(logcat, "We are terminal hop for path request!"); + return handle_inner_request(std::move(m), std::move(payload), std::move(hop)); } - auto hop_is_rx = hop->rxid() == hopid; + auto hop_is_rx = hop->rxid() == hop_id; const auto& next_id = hop_is_rx ? hop->txid() : hop->rxid(); const auto& next_router = hop_is_rx ? hop->upstream() : hop->downstream(); - std::string new_payload = ONION::serialize_hop(next_id.to_view(), symmnonce, payload); + std::string new_payload = ONION::serialize_hop(next_id.to_view(), nonce, std::move(payload)); send_control_message( next_router, - "path_control"s, + "path_control", std::move(new_payload), - [hop_weak = hop->weak_from_this(), hopid, prev_message = std::move(m)]( + [hop_weak = hop->weak_from_this(), hop_id, prev_message = std::move(m)]( oxen::quic::message response) mutable { auto hop = hop_weak.lock(); if (not hop) - return; + { + log::warning(logcat, "Received response to path control message with non-existant TransitHop!"); + return prev_message.respond(messages::ERROR_RESPONSE, true); + } if (response.timed_out) - log::debug(logcat, "Path control message timed out!"); + { + log::info(logcat, "Path control message returned as time out!"); + return prev_message.respond(messages::TIMEOUT_RESPONSE, true); + } - ustring hop_id, nonce, payload; + HopID hop_id; + SymmNonce nonce; + std::string payload; try { - oxenc::bt_dict_consumer btdc{response.body()}; - std::tie(hop_id, nonce, payload) = ONION::deserialize_hop(btdc); + std::tie(hop_id, nonce, payload) = ONION::deserialize_hop(oxenc::bt_dict_consumer{response.body()}); } catch (const std::exception& e) { log::warning(logcat, "Exception: {}", e.what()); - return; + return prev_message.respond(messages::ERROR_RESPONSE, true); + ; } - auto symmnonce = SymmNonce{nonce.data()}; - auto resp_payload = ONION::serialize_hop(hop_id, symmnonce, payload); + auto resp_payload = ONION::serialize_hop(hop_id.to_view(), nonce, std::move(payload)); prev_message.respond(std::move(resp_payload), false); }); } + void LinkManager::handle_path_data_message(bstring message) + { + HopID hop_id; + std::string payload; + SymmNonce nonce; + + try + { + std::tie(hop_id, nonce, payload) = ONION::deserialize_hop(oxenc::bt_dict_consumer{message}); + } + catch (const std::exception& e) + { + log::warning(logcat, "Exception: {}", e.what()); + return; + } + + auto hop = _router.path_context()->get_transit_hop(hop_id); + + if (not hop) + { + log::warning(logcat, "Received path data with unknown next hop (ID: {})", hop_id); + return; + } + + nonce = crypto::onion( + reinterpret_cast(payload.data()), payload.size(), hop->shared, nonce, hop->nonceXOR); + + // if terminal hop, pass to the correct path expecting to receive this message + if (hop->terminal_hop) + { + NetworkAddress sender; + bstring data; + + try + { + oxenc::bt_dict_consumer btdc{payload}; + std::tie(sender, data) = PATH::DATA::deserialize(btdc); + + if (auto session = _router.session_endpoint()->get_session(sender)) + { + session->recv_path_data_message(std::move(data)); + } + else + { + log::warning(logcat, "Could not find session (remote:{}) to relay path data message!", sender); + } + } + catch (const std::exception& e) + { + log::warning(logcat, "Exception: {}", e.what()); + } + } + else + { + // if not terminal hop, relay datagram onwards + auto hop_is_rx = hop->rxid() == hop_id; + + const auto& next_id = hop_is_rx ? hop->txid() : hop->rxid(); + const auto& next_router = hop_is_rx ? hop->upstream() : hop->downstream(); + + std::string new_payload = ONION::serialize_hop(next_id.to_view(), nonce, std::move(payload)); + + send_data_message(next_router, std::move(new_payload)); + } + } + void LinkManager::handle_inner_request( oxen::quic::message m, std::string payload, std::shared_ptr hop) { @@ -1432,8 +1447,7 @@ namespace llarp try { - oxenc::bt_dict_consumer btdc{payload}; - std::tie(endpoint, body) = PATH::CONTROL::deserialize(btdc); + std::tie(endpoint, body) = PATH::CONTROL::deserialize(oxenc::bt_dict_consumer{payload}); } catch (const std::exception& e) { @@ -1446,10 +1460,12 @@ namespace llarp if (itr == path_requests.end()) { - log::info(logcat, "Received path control request \"{}\", which has no handler.", endpoint); + log::warning(logcat, "Received path control request `{}`, which has no handler.", endpoint); return; } + log::debug(logcat, "Received path control request `{}`", endpoint); + auto respond = [m = std::move(m), hop_weak = hop->weak_from_this()](std::string response) mutable { auto hop = hop_weak.lock(); if (not hop) diff --git a/llarp/link/link_manager.hpp b/llarp/link/link_manager.hpp index f48248b2d9..2685ebe52f 100644 --- a/llarp/link/link_manager.hpp +++ b/llarp/link/link_manager.hpp @@ -278,7 +278,7 @@ namespace llarp void handle_convo_intro(oxen::quic::message); // These requests come over a path (as a "path_control" request), - // may or may not need to make a request to another relay, + // we may or may not need to make a request to another relay, // then respond (onioned) back along the path. std::unordered_map< std::string_view, diff --git a/llarp/messages/path.hpp b/llarp/messages/path.hpp index 155327763d..b9d73af87d 100644 --- a/llarp/messages/path.hpp +++ b/llarp/messages/path.hpp @@ -37,6 +37,26 @@ namespace llarp return std::move(btdp).str(); } + inline static std::tuple deserialize_hop(oxenc::bt_dict_consumer&& btdc) + { + HopID hop_id; + std::string payload; + SymmNonce nonce; + + try + { + hop_id.from_string(btdc.require("k")); + nonce.from_string(btdc.require("n")); + payload = btdc.require("x"); + } + catch (const std::exception& e) + { + throw std::runtime_error{"Exception caught deserializing onion data:{}"_format(e.what())}; + } + + return {std::move(hop_id), std::move(nonce), std::move(payload)}; + } + inline static std::tuple deserialize_hop(oxenc::bt_dict_consumer& btdc) { ustring hopid, nonce, payload; @@ -186,46 +206,46 @@ namespace llarp { /** Fields for transmitting Path Control: - 'e' : request endpoint being invoked - - 'r' : request body + - 'p' : request payload */ - inline static std::string serialize(std::string endpoint, std::string body) + inline static std::string serialize(std::string endpoint, std::string payload) { oxenc::bt_dict_producer btdp; btdp.append("e", endpoint); - btdp.append("r", body); + btdp.append("p", payload); return std::move(btdp).str(); } - inline static std::tuple deserialize(oxenc::bt_dict_consumer& btdc) + inline static std::tuple deserialize(oxenc::bt_dict_consumer&& btdc) { - std::string endpoint, body; + std::string endpoint, payload; try { endpoint = btdc.require("e"); - body = btdc.require("r"); + payload = btdc.require("p"); } catch (const std::exception& e) { throw std::runtime_error{"Exception caught deserializing path control:{}"_format(e.what())}; } - return {std::move(endpoint), std::move(body)}; + return {std::move(endpoint), std::move(payload)}; } } // namespace CONTROL namespace DATA { /** Fields for transmitting Path Data: - - 'b' : request/command body - - 's' : RouterID of sender + - 'i' : RouterID of sender + - 'p' : request/command payload NOTE: more fields may be added later as needed, hence the namespacing */ - inline static std::string serialize(std::string body, const RouterID& local) + inline static std::string serialize(std::string payload, const RouterID& local) { oxenc::bt_dict_producer btdp; - btdp.append("b", body); - btdp.append("s", local.to_view()); + btdp.append("i", local.to_view()); + btdp.append("p", payload); return std::move(btdp).str(); } @@ -236,8 +256,8 @@ namespace llarp try { - body = btdc.require("b"); - remote.from_string(btdc.require("s")); + remote.from_string(btdc.require("i")); + body = btdc.require("p"); auto sender = NetworkAddress::from_pubkey(remote, true); return {std::move(sender), std::move(body)}; diff --git a/llarp/path/path.cpp b/llarp/path/path.cpp index cfc17f3f83..bc02746b32 100644 --- a/llarp/path/path.cpp +++ b/llarp/path/path.cpp @@ -144,25 +144,21 @@ namespace llarp::path // _role &= ePathRoleExit; } - std::string Path::make_path_message(std::string&& inner_payload) + std::string Path::make_path_message(std::string inner_payload) { - int n_hops = static_cast(hops.size()); - std::string payload{std::move(inner_payload)}; + auto nonce = SymmNonce::make_random(); - // Working from final hop to hop 1, we onion encrypt the message payload with each - // hop's shared secret (this was derived via DH KEM in path building). The encrypted - // payload will then be bt-serialized, and then encrypted/serialized for the next hop. - for (int i = n_hops - 1; i >= 0; --i) + for (const auto& hop : hops) { - auto& hop = hops[i]; - - crypto::onion( - reinterpret_cast(payload.data()), payload.size(), hop.shared, hop.nonce, hop.nonceXOR); - - payload = ONION::serialize_hop(hop.upstream.to_view(), hop.nonce, payload); + nonce = crypto::onion( + reinterpret_cast(inner_payload.data()), + inner_payload.size(), + hop.shared, + hop.nonce, + hop.nonce); } - return payload; + return ONION::serialize_hop(upstream_txid().to_view(), nonce, std::move(inner_payload)); } bool Path::send_path_data_message(std::string data) @@ -184,32 +180,41 @@ namespace llarp::path std::move(outer_payload), [response_cb = std::move(func), weak = weak_from_this()](oxen::quic::message m) mutable { auto self = weak.lock(); - // TODO: do we want to allow empty callback here? - if ((not self) or (not response_cb)) + if (not self) + { + log::warning(logcat, "Received response to path control message with non-existant path!"); return; + } - if (m.timed_out) + // TODO: DISCUSS: do we want to allow empty callback here? + if (not response_cb) { - response_cb(messages::TIMEOUT_RESPONSE); + log::warning(logcat, "Received response to path control message with no response callback!"); return; } - ustring hop_id_str, symmnonce, payload; + log::debug(logcat, "Received response to path control message..."); + + if (m.timed_out) + { + log::info(logcat, "Path control message returned as time out!"); + return response_cb(messages::TIMEOUT_RESPONSE); + } + + HopID hop_id; + SymmNonce nonce; + std::string payload; try { - oxenc::bt_dict_consumer btdc{m.body()}; - std::tie(hop_id_str, symmnonce, payload) = ONION::deserialize_hop(btdc); + std::tie(hop_id, nonce, payload) = ONION::deserialize_hop(oxenc::bt_dict_consumer{m.body()}); } catch (const std::exception& e) { log::warning(logcat, "Error parsing path control message response: {}", e.what()); - response_cb(messages::ERROR_RESPONSE); - return; + return response_cb(messages::ERROR_RESPONSE); } - SymmNonce nonce{symmnonce.data()}; - for (const auto& hop : self->hops) { nonce = crypto::onion( @@ -220,10 +225,8 @@ namespace llarp::path 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. + // TODO: DISCUSS: + // Parsing and handling of the contents (errors, etc.) is the currently responsibility of the callback response_cb(std::string{reinterpret_cast(payload.data()), payload.size()}); }); } diff --git a/llarp/path/path.hpp b/llarp/path/path.hpp index 70c5978652..1222534cf6 100644 --- a/llarp/path/path.hpp +++ b/llarp/path/path.hpp @@ -161,7 +161,7 @@ namespace llarp static constexpr bool to_string_formattable = true; private: - std::string make_path_message(std::string&& payload); + std::string make_path_message(std::string payload); bool SendLatencyMessage(Router* r); diff --git a/llarp/router/router.cpp b/llarp/router/router.cpp index 951cff1a18..b4696c71d7 100644 --- a/llarp/router/router.cpp +++ b/llarp/router/router.cpp @@ -218,7 +218,7 @@ namespace llarp } else { - // _session_endpoint->start_tickers(); + _session_endpoint->start_tickers(); // Resolve needed ONS values now that we have the necessary things prefigured _session_endpoint->resolve_ons_mappings(); } diff --git a/llarp/session/session.cpp b/llarp/session/session.cpp index 5fb380ce99..87c76b1913 100644 --- a/llarp/session/session.cpp +++ b/llarp/session/session.cpp @@ -185,6 +185,7 @@ namespace llarp::session void OutboundSession::path_died(std::shared_ptr p) { + log::debug(logcat, "{} called", __PRETTY_FUNCTION__); p->rebuild(); } @@ -210,6 +211,7 @@ namespace llarp::session void OutboundSession::path_build_succeeded(std::shared_ptr p) { + log::debug(logcat, "{} called", __PRETTY_FUNCTION__); path::PathHandler::path_build_succeeded(p); // TODO: why the fuck did we used to do this here...?