mirror of
https://github.com/oxen-io/oxen-storage-server.git
synced 2023-12-13 21:00:26 +01:00
New internal SS-to-SS onion req endpoint
Replaces the sn.onion_req_v2 OMQ endpoint with a new sn.onion_request that takes an extendable bencoded dict (the same as used extensively in oxen-core and lokinet), thus allowing us to pass fields such as hop number and encryption type in the request, remaining compact (binary data has no overhead), and allows for future additions without requiring a new endpoint. The new endpoint activates for SN-to-SN onion data at HF18; before then the sn.onion_req_v2 is still used and remains backwards compatible (but cannot be extended with encryption type or hop info). Currently on the wire we have four fields: p - end encrypted payload (required) ek - the ephemeral key (required) et - the encryption type (optional, aes-gcm if not provided) nh - the hop number, which get incremented on each hop Max path length is limited to 15, to allow the client to choose to obscure it's path knowledge somewhat by using a randomized starting hop position from `[0, 15-actual]`
This commit is contained in:
parent
c0e7deef2f
commit
10b9d5accb
|
@ -28,12 +28,6 @@ calculate_shared_secret(const x25519_seckey& seckey,
|
|||
return secret;
|
||||
}
|
||||
|
||||
EncryptType parse_enc_type(std::string_view enc_type) {
|
||||
if (enc_type == "aes-gcm" || enc_type == "gcm") return EncryptType::aes_gcm;
|
||||
if (enc_type == "aes-cbc" || enc_type == "cbc") return EncryptType::aes_cbc;
|
||||
throw std::runtime_error{"Invalid encryption type " + std::string{enc_type}};
|
||||
}
|
||||
|
||||
std::basic_string_view<unsigned char> to_uchar(std::string_view sv) {
|
||||
return {reinterpret_cast<const unsigned char*>(sv.data()), sv.size()};
|
||||
}
|
||||
|
@ -68,6 +62,12 @@ using aes256cbc_ctx_ptr = std::unique_ptr<EVP_CIPHER_CTX, aes256_evp_deleter>;
|
|||
|
||||
}
|
||||
|
||||
EncryptType parse_enc_type(std::string_view enc_type) {
|
||||
if (enc_type == "aes-gcm" || enc_type == "gcm") return EncryptType::aes_gcm;
|
||||
if (enc_type == "aes-cbc" || enc_type == "cbc") return EncryptType::aes_cbc;
|
||||
throw std::runtime_error{"Invalid encryption type " + std::string{enc_type}};
|
||||
}
|
||||
|
||||
std::string ChannelEncryption::encrypt(EncryptType type, std::string_view plaintext, const x25519_pubkey& pubkey) const {
|
||||
switch (type) {
|
||||
case EncryptType::aes_gcm: return encrypt_gcm(plaintext, pubkey);
|
||||
|
|
|
@ -498,32 +498,43 @@ void connection_t::process_onion_req_v2() {
|
|||
// Need to make sure we are not blocking waiting for the response
|
||||
delay_response_ = true;
|
||||
|
||||
auto on_response = [wself = weak_from_this()](oxen::Response res) {
|
||||
OXEN_LOG(debug, "Got an onion response as edge node");
|
||||
OnionRequestMetadata data{
|
||||
x25519_pubkey{},
|
||||
[wself = weak_from_this()](oxen::Response res) {
|
||||
OXEN_LOG(debug, "Got an onion response as edge node");
|
||||
|
||||
auto self = wself.lock();
|
||||
if (!self) {
|
||||
OXEN_LOG(debug,
|
||||
"Connection is no longer valid, dropping onion response");
|
||||
return;
|
||||
}
|
||||
auto self = wself.lock();
|
||||
if (!self) {
|
||||
OXEN_LOG(debug,
|
||||
"Connection is no longer valid, dropping onion response");
|
||||
return;
|
||||
}
|
||||
|
||||
self->body_stream_ << res.message();
|
||||
self->response_.result(static_cast<int>(res.status()));
|
||||
self->body_stream_ << res.message();
|
||||
self->response_.result(static_cast<int>(res.status()));
|
||||
|
||||
self->write_response();
|
||||
self->write_response();
|
||||
},
|
||||
0, // hopno
|
||||
EncryptType::aes_gcm,
|
||||
};
|
||||
|
||||
try {
|
||||
|
||||
auto [ciphertext, json_req] = parse_combined_payload(req.body());
|
||||
|
||||
auto ephem_key = extract_x25519_from_hex(
|
||||
data.ephem_key = extract_x25519_from_hex(
|
||||
json_req.at("ephemeral_key").get_ref<const std::string&>());
|
||||
|
||||
if (auto it = json_req.find("enc_type"); it != json_req.end())
|
||||
data.enc_type = parse_enc_type(it->get_ref<const std::string&>());
|
||||
|
||||
// Allows a fake starting hop number (to make it harder for intermediate hops to know where
|
||||
// they are). If omitted, defaults to 0.
|
||||
if (auto it = json_req.find("hop_no"); it != json_req.end())
|
||||
data.hop_no = std::max(0, it->get<int>());
|
||||
|
||||
service_node_.record_onion_request();
|
||||
request_handler_.process_onion_req(std::move(ciphertext), ephem_key,
|
||||
on_response);
|
||||
request_handler_.process_onion_req(ciphertext, std::move(data));
|
||||
|
||||
} catch (const std::exception& e) {
|
||||
auto msg = fmt::format("Error parsing onion request: {}",
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "oxen_common.h"
|
||||
#include "oxen_logger.h"
|
||||
#include "oxend_key.h"
|
||||
#include "channel_encryption.hpp"
|
||||
#include "oxenmq/connections.h"
|
||||
#include "oxenmq/oxenmq.h"
|
||||
#include "request_handler.h"
|
||||
|
@ -105,11 +106,12 @@ void OxenmqServer::handle_ping(oxenmq::Message& message) {
|
|||
message.send_reply("pong");
|
||||
}
|
||||
|
||||
void OxenmqServer::handle_onion_request(oxenmq::Message& message) {
|
||||
void OxenmqServer::handle_onion_request(
|
||||
std::string_view payload,
|
||||
OnionRequestMetadata&& data,
|
||||
oxenmq::Message::DeferredSend send) {
|
||||
|
||||
OXEN_LOG(debug, "Got an onion request over OxenMQ");
|
||||
|
||||
auto on_response = [send=message.send_later()](oxen::Response res) {
|
||||
data.cb = [send](oxen::Response res) {
|
||||
if (OXEN_LOG_ENABLED(trace))
|
||||
OXEN_LOG(trace, "on response: {}...", to_string(res).substr(0, 100));
|
||||
|
||||
|
@ -118,21 +120,54 @@ void OxenmqServer::handle_onion_request(oxenmq::Message& message) {
|
|||
std::move(res).message());
|
||||
};
|
||||
|
||||
if (data.hop_no > MAX_ONION_HOPS)
|
||||
return data.cb({Status::BAD_REQUEST, "onion request max path length exceeded"});
|
||||
|
||||
request_handler_->process_onion_req(payload, std::move(data));
|
||||
}
|
||||
|
||||
void OxenmqServer::handle_onion_request(oxenmq::Message& message) {
|
||||
std::pair<std::string_view, OnionRequestMetadata> data;
|
||||
try {
|
||||
if (message.data.size() != 1)
|
||||
throw std::runtime_error{"expected 1 part, got " + std::to_string(message.data.size())};
|
||||
|
||||
data = decode_onion_data(message.data[0]);
|
||||
} catch (const std::exception& e) {
|
||||
auto msg = "Invalid internal onion request: "s + e.what();
|
||||
OXEN_LOG(error, "{}", msg);
|
||||
message.send_reply(
|
||||
std::to_string(static_cast<int>(Status::BAD_REQUEST)), msg);
|
||||
return;
|
||||
}
|
||||
|
||||
handle_onion_request(data.first, std::move(data.second), message.send_later());
|
||||
}
|
||||
|
||||
void OxenmqServer::handle_onion_req_v2(oxenmq::Message& message) {
|
||||
|
||||
OXEN_LOG(debug, "Got a v2 onion request over OxenMQ");
|
||||
|
||||
const int bad_code = static_cast<int>(Status::BAD_REQUEST);
|
||||
if (message.data.size() != 2) {
|
||||
OXEN_LOG(error, "Expected 2 message parts, got {}",
|
||||
message.data.size());
|
||||
return on_response({Status::BAD_REQUEST, "Incorrect number of request parts"});
|
||||
message.send_reply(std::to_string(bad_code),
|
||||
"Incorrect number of onion request message parts");
|
||||
return;
|
||||
}
|
||||
|
||||
auto eph_key = extract_x25519_from_hex(message.data[0]);
|
||||
if (!eph_key) {
|
||||
OXEN_LOG(error, "no ephemeral key in omq onion request");
|
||||
return on_response({Status::BAD_REQUEST, "Missing ephemeral key"});
|
||||
message.send_reply(std::to_string(bad_code), "Missing ephemeral key");
|
||||
return;
|
||||
}
|
||||
const auto& ciphertext = message.data[1];
|
||||
|
||||
request_handler_->process_onion_req(
|
||||
std::string{ciphertext}, *eph_key, on_response);
|
||||
handle_onion_request(
|
||||
message.data[1], // ciphertext
|
||||
{*eph_key, nullptr, 1 /* hopno */, EncryptType::aes_gcm},
|
||||
message.send_later());
|
||||
}
|
||||
|
||||
void OxenmqServer::handle_get_logs(oxenmq::Message& message) {
|
||||
|
@ -218,7 +253,9 @@ OxenmqServer::OxenmqServer(
|
|||
std::to_string(static_cast<int>(Status::BAD_REQUEST)),
|
||||
"onion requests v1 not supported");
|
||||
})
|
||||
.add_request_command("onion_req_v2", [this](auto& m) { handle_onion_request(m); })
|
||||
// TODO: Backwards compat, only used up until HF18
|
||||
.add_request_command("onion_req_v2", [this](auto& m) { handle_onion_req_v2(m); })
|
||||
.add_request_command("onion_request", [this](auto& m) { handle_onion_request(m); })
|
||||
;
|
||||
|
||||
omq_.add_category("service", oxenmq::AuthLevel::admin)
|
||||
|
@ -268,4 +305,39 @@ void OxenmqServer::init(ServiceNode* sn, RequestHandler* rh, oxenmq::address oxe
|
|||
connect_oxend(oxend_rpc);
|
||||
}
|
||||
|
||||
std::string OxenmqServer::encode_onion_data(std::string_view payload, const OnionRequestMetadata& data) {
|
||||
return oxenmq::bt_serialize<oxenmq::bt_dict>({
|
||||
{"d", payload},
|
||||
{"ek", data.ephem_key.view()},
|
||||
{"et", to_string(data.enc_type)},
|
||||
{"nh", data.hop_no},
|
||||
});
|
||||
}
|
||||
|
||||
std::pair<std::string_view, OnionRequestMetadata> OxenmqServer::decode_onion_data(std::string_view data) {
|
||||
// NB: stream parsing here is alphabetical
|
||||
std::pair<std::string_view, OnionRequestMetadata> result;
|
||||
auto& [payload, meta] = result;
|
||||
oxenmq::bt_dict_consumer d{data};
|
||||
if (!d.skip_until("d"))
|
||||
throw std::runtime_error{"required data payload not found"};
|
||||
payload = d.consume_string_view();
|
||||
|
||||
if (!d.skip_until("ek"))
|
||||
throw std::runtime_error{"ephemeral key not found"};
|
||||
meta.ephem_key = x25519_pubkey::from_bytes(d.consume_string_view());
|
||||
|
||||
if (d.skip_until("et"))
|
||||
meta.enc_type = parse_enc_type(d.consume_string_view());
|
||||
else
|
||||
meta.enc_type = EncryptType::aes_gcm;
|
||||
|
||||
if (d.skip_until("nh"))
|
||||
meta.hop_no = d.consume_integer<int>();
|
||||
if (meta.hop_no < 1)
|
||||
meta.hop_no = 1;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace oxen
|
||||
|
|
|
@ -15,6 +15,7 @@ namespace oxen {
|
|||
struct oxend_key_pair_t;
|
||||
class ServiceNode;
|
||||
class RequestHandler;
|
||||
struct OnionRequestMetadata;
|
||||
|
||||
void omq_logger(oxenmq::LogLevel level, const char* file, int line,
|
||||
std::string message);
|
||||
|
@ -39,8 +40,17 @@ class OxenmqServer {
|
|||
void handle_sn_proxy_exit(oxenmq::Message& message);
|
||||
|
||||
// Called for the sn.onion_req_v2 endpoint
|
||||
void handle_onion_req_v2(oxenmq::Message& message);
|
||||
|
||||
// Called starting at HF18 for SS-to-SS onion requests
|
||||
void handle_onion_request(oxenmq::Message& message);
|
||||
|
||||
// Handles a decoded onion request
|
||||
void handle_onion_request(
|
||||
std::string_view payload,
|
||||
OnionRequestMetadata&& data,
|
||||
oxenmq::Message::DeferredSend send);
|
||||
|
||||
// sn.ping - sent by SNs to ping each other.
|
||||
void handle_ping(oxenmq::Message& message);
|
||||
|
||||
|
@ -84,6 +94,12 @@ class OxenmqServer {
|
|||
assert(oxend_conn_);
|
||||
omq_.send(oxend_conn(), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
// Encodes the onion request data that we send for internal SN-to-SN onion requests starting at
|
||||
// HF18.
|
||||
static std::string encode_onion_data(std::string_view payload, const OnionRequestMetadata& data);
|
||||
// Decodes onion request data; throws if invalid formatted or missing required fields.
|
||||
static std::pair<std::string_view, OnionRequestMetadata> decode_onion_data(std::string_view data);
|
||||
};
|
||||
|
||||
} // namespace oxen
|
||||
|
|
|
@ -55,7 +55,8 @@ auto process_inner_request(std::string plaintext) -> ParsedInfo {
|
|||
ctext = std::move(ciphertext);
|
||||
next = ed25519_pubkey::from_hex(
|
||||
inner_json.at("destination").get_ref<const std::string&>());
|
||||
eph_key = inner_json.at("ephemeral_key").get<std::string>();
|
||||
eph_key = x25519_pubkey::from_hex(
|
||||
inner_json.at("ephemeral_key").get_ref<const std::string&>());
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
OXEN_LOG(debug, "Error parsing inner JSON in onion request: {}",
|
||||
|
@ -69,11 +70,12 @@ auto process_inner_request(std::string plaintext) -> ParsedInfo {
|
|||
static auto
|
||||
process_ciphertext_v2(const ChannelEncryption& decryptor,
|
||||
std::string_view ciphertext,
|
||||
const x25519_pubkey& ephem_key) -> ParsedInfo {
|
||||
const x25519_pubkey& ephem_key,
|
||||
EncryptType enc_type) -> ParsedInfo {
|
||||
std::optional<std::string> plaintext;
|
||||
|
||||
try {
|
||||
plaintext = decryptor.decrypt(EncryptType::aes_gcm, ciphertext, ephem_key);
|
||||
plaintext = decryptor.decrypt(enc_type, ciphertext, ephem_key);
|
||||
} catch (const std::exception& e) {
|
||||
OXEN_LOG(debug, "Error decrypting an onion request: {}", e.what());
|
||||
}
|
||||
|
@ -125,46 +127,44 @@ static auto make_status(std::string_view status) -> oxen::Status {
|
|||
|
||||
// FIXME: why are these method definitions *here* instead of request_handler.cpp?
|
||||
void RequestHandler::process_onion_req(std::string_view ciphertext,
|
||||
const x25519_pubkey& ephem_key,
|
||||
std::function<void(oxen::Response)> cb) {
|
||||
OnionRequestMetadata data) {
|
||||
if (!service_node_.snode_ready()) {
|
||||
auto msg =
|
||||
fmt::format("Snode not ready: {}",
|
||||
service_node_.own_address().pubkey_ed25519);
|
||||
return cb({Status::SERVICE_UNAVAILABLE, std::move(msg)});
|
||||
return data.cb({Status::SERVICE_UNAVAILABLE, std::move(msg)});
|
||||
}
|
||||
|
||||
OXEN_LOG(debug, "process_onion_req");
|
||||
|
||||
var::visit([&](auto&& x) { process_onion_req(std::move(x), ephem_key, std::move(cb)); },
|
||||
process_ciphertext_v2(channel_cipher_, ciphertext, ephem_key));
|
||||
var::visit([&](auto&& x) { process_onion_req(std::move(x), std::move(data)); },
|
||||
process_ciphertext_v2(channel_cipher_, ciphertext, data.ephem_key, data.enc_type));
|
||||
}
|
||||
|
||||
void RequestHandler::process_onion_req(FinalDestinationInfo&& info,
|
||||
const x25519_pubkey& ephem_key, std::function<void(oxen::Response)> cb) {
|
||||
OnionRequestMetadata&& data) {
|
||||
OXEN_LOG(debug, "We are the final destination in the onion request!");
|
||||
|
||||
process_onion_exit(
|
||||
ephem_key, info.body,
|
||||
[this, ephem_key, cb = std::move(cb), json = info.json, b64 = info.base64]
|
||||
info.body,
|
||||
[this, data = std::move(data), json = info.json, b64 = info.base64]
|
||||
(oxen::Response res) {
|
||||
cb(wrap_proxy_response(std::move(res), ephem_key, EncryptType::aes_gcm, json, b64));
|
||||
data.cb(wrap_proxy_response(std::move(res), data.ephem_key, data.enc_type, json, b64));
|
||||
});
|
||||
}
|
||||
|
||||
void RequestHandler::process_onion_req(RelayToNodeInfo&& info,
|
||||
const x25519_pubkey& ephem_key, std::function<void(oxen::Response)> cb) {
|
||||
OnionRequestMetadata&& data) {
|
||||
auto& [payload, ekey, dest] = info;
|
||||
|
||||
auto dest_node = service_node_.find_node(dest);
|
||||
if (!dest_node) {
|
||||
auto msg = fmt::format("Next node not found: {}", dest);
|
||||
OXEN_LOG(warn, "{}", msg);
|
||||
return cb({Status::BAD_GATEWAY, std::move(msg)});
|
||||
return data.cb({Status::BAD_GATEWAY, std::move(msg)});
|
||||
}
|
||||
|
||||
|
||||
auto on_response = [cb=std::move(cb)](bool success,
|
||||
auto on_response = [cb=std::move(data.cb)](bool success,
|
||||
std::vector<std::string> data) {
|
||||
// Processing the result we got from upstream
|
||||
|
||||
|
@ -190,8 +190,9 @@ void RequestHandler::process_onion_req(RelayToNodeInfo&& info,
|
|||
|
||||
OXEN_LOG(debug, "send_onion_to_sn, sn: {}", dest_node->pubkey_legacy);
|
||||
|
||||
service_node_.send_onion_to_sn_v2(
|
||||
*dest_node, std::move(payload), ekey, std::move(on_response));
|
||||
data.ephem_key = ekey;
|
||||
service_node_.send_onion_to_sn(
|
||||
*dest_node, std::move(payload), std::move(data), std::move(on_response));
|
||||
}
|
||||
|
||||
bool is_server_url_allowed(std::string_view url) {
|
||||
|
@ -202,30 +203,30 @@ bool is_server_url_allowed(std::string_view url) {
|
|||
}
|
||||
|
||||
void RequestHandler::process_onion_req(RelayToServerInfo&& info,
|
||||
const x25519_pubkey& ephem_key, std::function<void(oxen::Response)> cb) {
|
||||
OnionRequestMetadata&& data) {
|
||||
OXEN_LOG(debug, "We are to forward the request to url: {}{}",
|
||||
info.host, info.target);
|
||||
|
||||
// Forward the request to url but only if it ends in `/lsrpc`
|
||||
if (is_server_url_allowed(info.target))
|
||||
return process_onion_to_url(info.protocol, std::move(info.host), info.port,
|
||||
std::move(info.target), std::move(info.payload), std::move(cb));
|
||||
std::move(info.target), std::move(info.payload), std::move(data.cb));
|
||||
|
||||
return cb(wrap_proxy_response({Status::BAD_REQUEST, "Invalid url"},
|
||||
ephem_key, EncryptType::aes_gcm));
|
||||
return data.cb(wrap_proxy_response({Status::BAD_REQUEST, "Invalid url"},
|
||||
data.ephem_key, data.enc_type));
|
||||
}
|
||||
|
||||
void RequestHandler::process_onion_req(ProcessCiphertextError&& error,
|
||||
const x25519_pubkey& ephem_key, std::function<void(oxen::Response)> cb) {
|
||||
OnionRequestMetadata&& data) {
|
||||
|
||||
switch (error) {
|
||||
case ProcessCiphertextError::INVALID_CIPHERTEXT:
|
||||
// Should this error be propagated back to the client? (No, if we
|
||||
// couldn't decrypt, we probably won't be able to encrypt either.)
|
||||
return cb({Status::BAD_REQUEST, "Invalid ciphertext"});
|
||||
return data.cb({Status::BAD_REQUEST, "Invalid ciphertext"});
|
||||
case ProcessCiphertextError::INVALID_JSON:
|
||||
return cb(wrap_proxy_response({Status::BAD_REQUEST, "Invalid json"},
|
||||
ephem_key, EncryptType::aes_gcm));
|
||||
return data.cb(wrap_proxy_response({Status::BAD_REQUEST, "Invalid json"},
|
||||
data.ephem_key, data.enc_type));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,11 @@
|
|||
|
||||
namespace oxen {
|
||||
|
||||
// Maximum onion request hops we'll accept before we return an error; this is deliberately larger
|
||||
// than we actually use so that the client can choose to obscure hop positioning by starting at
|
||||
// somewhere higher than 0.
|
||||
inline constexpr int MAX_ONION_HOPS = 15;
|
||||
|
||||
using CiphertextPlusJson = std::pair<std::string, nlohmann::json>;
|
||||
|
||||
/// The request is to be forwarded to another SS node
|
||||
|
@ -14,7 +19,7 @@ struct RelayToNodeInfo {
|
|||
/// Inner ciphertext for next node
|
||||
std::string ciphertext;
|
||||
// Key to be forwarded to next node for decryption
|
||||
std::string ephemeral_key;
|
||||
x25519_pubkey ephemeral_key;
|
||||
// Next node's ed25519 key
|
||||
ed25519_pubkey next_node;
|
||||
};
|
||||
|
|
|
@ -349,7 +349,7 @@ Response RequestHandler::process_retrieve(const json& params) {
|
|||
}
|
||||
|
||||
void RequestHandler::process_client_req(
|
||||
const std::string& req_json, std::function<void(oxen::Response)> cb) {
|
||||
std::string_view req_json, std::function<void(oxen::Response)> cb) {
|
||||
|
||||
OXEN_LOG(trace, "process_client_req str <{}>", req_json);
|
||||
|
||||
|
@ -469,7 +469,7 @@ void RequestHandler::process_lns_request(
|
|||
}
|
||||
|
||||
void RequestHandler::process_onion_exit(
|
||||
const x25519_pubkey& eph_key, const std::string& body,
|
||||
std::string_view body,
|
||||
std::function<void(oxen::Response)> cb) {
|
||||
|
||||
OXEN_LOG(debug, "Processing onion exit!");
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include "channel_encryption.hpp"
|
||||
#include "onion_processing.h"
|
||||
#include "oxen_common.h"
|
||||
#include "oxend_key.h"
|
||||
|
@ -12,8 +13,6 @@
|
|||
|
||||
namespace oxen {
|
||||
|
||||
class ChannelEncryption;
|
||||
enum struct EncryptType;
|
||||
class ServiceNode;
|
||||
|
||||
enum class Status {
|
||||
|
@ -77,6 +76,13 @@ std::string computeMessageHash(const std::string& timestamp,
|
|||
const std::string& recipient,
|
||||
const std::string& data);
|
||||
|
||||
struct OnionRequestMetadata {
|
||||
x25519_pubkey ephem_key;
|
||||
std::function<void(oxen::Response)> cb;
|
||||
int hop_no = 0;
|
||||
EncryptType enc_type = EncryptType::aes_gcm;
|
||||
};
|
||||
|
||||
class RequestHandler {
|
||||
|
||||
boost::asio::io_context& ioc_;
|
||||
|
@ -105,8 +111,7 @@ class RequestHandler {
|
|||
// Query the database and return requested messages
|
||||
Response process_retrieve(const nlohmann::json& params);
|
||||
|
||||
void process_onion_exit(const x25519_pubkey& eph_key,
|
||||
const std::string& payload,
|
||||
void process_onion_exit(std::string_view payload,
|
||||
std::function<void(oxen::Response)> cb);
|
||||
|
||||
void process_lns_request(std::string name_hash,
|
||||
|
@ -119,7 +124,7 @@ class RequestHandler {
|
|||
const ChannelEncryption& ce);
|
||||
|
||||
// Process all Session client requests
|
||||
void process_client_req(const std::string& req_json,
|
||||
void process_client_req(std::string_view req_json,
|
||||
std::function<void(oxen::Response)> cb);
|
||||
|
||||
// Forwards a request to oxend RPC. `params` should contain:
|
||||
|
@ -150,17 +155,12 @@ class RequestHandler {
|
|||
std::function<void(oxen::Response)> cb);
|
||||
|
||||
// The result will arrive asynchronously, so it needs a callback handler
|
||||
void process_onion_req(std::string_view ciphertext,
|
||||
const x25519_pubkey& ephem_key,
|
||||
std::function<void(oxen::Response)> cb);
|
||||
void process_onion_req(std::string_view ciphertext, OnionRequestMetadata data);
|
||||
|
||||
void process_onion_req(FinalDestinationInfo&& res,
|
||||
const x25519_pubkey& ekey, std::function<void(oxen::Response)> cb);
|
||||
void process_onion_req(RelayToNodeInfo&& res,
|
||||
const x25519_pubkey& ekey, std::function<void(oxen::Response)> cb);
|
||||
void process_onion_req(RelayToServerInfo&& res,
|
||||
const x25519_pubkey& ekey, std::function<void(oxen::Response)> cb);
|
||||
void process_onion_req(ProcessCiphertextError&& res,
|
||||
const x25519_pubkey& ekey, std::function<void(oxen::Response)> cb);
|
||||
private:
|
||||
void process_onion_req(FinalDestinationInfo&& res, OnionRequestMetadata&& data);
|
||||
void process_onion_req(RelayToNodeInfo&& res, OnionRequestMetadata&& data);
|
||||
void process_onion_req(RelayToServerInfo&& res, OnionRequestMetadata&& data);
|
||||
void process_onion_req(ProcessCiphertextError&& res, OnionRequestMetadata&& data);
|
||||
};
|
||||
} // namespace oxen
|
||||
|
|
|
@ -284,24 +284,26 @@ bool ServiceNode::snode_ready(std::string* reason) {
|
|||
return problems.empty() || force_start_;
|
||||
}
|
||||
|
||||
void ServiceNode::send_onion_to_sn_v1(const sn_record_t& sn,
|
||||
const std::string& payload,
|
||||
const std::string& eph_key,
|
||||
ss_client::Callback cb) const {
|
||||
void ServiceNode::send_onion_to_sn(const sn_record_t& sn,
|
||||
std::string_view payload,
|
||||
OnionRequestMetadata&& data,
|
||||
ss_client::Callback cb) const {
|
||||
|
||||
lmq_server_->request(sn.pubkey_x25519.view(), "sn.onion_req", std::move(cb),
|
||||
oxenmq::send_option::request_timeout{30s}, eph_key,
|
||||
payload);
|
||||
}
|
||||
|
||||
void ServiceNode::send_onion_to_sn_v2(const sn_record_t& sn,
|
||||
const std::string& payload,
|
||||
const std::string& eph_key,
|
||||
ss_client::Callback cb) const {
|
||||
|
||||
lmq_server_->request(
|
||||
sn.pubkey_x25519.view(), "sn.onion_req_v2", std::move(cb),
|
||||
oxenmq::send_option::request_timeout{30s}, eph_key, payload);
|
||||
if (!hf_at_least(HARDFORK_OMQ_ONION_REQ_BENCODE)) {
|
||||
// use the _v2 endpoint up until the hf:
|
||||
lmq_server_->request(
|
||||
sn.pubkey_x25519.view(), "sn.onion_req_v2", std::move(cb),
|
||||
oxenmq::send_option::request_timeout{30s}, data.ephem_key.hex(), payload);
|
||||
} else {
|
||||
// Use the newer (v3, I suppose, though it's internal) where when bencode everything (which
|
||||
// is a bit more compact than sending the eph_key in hex, plus allows other metadata such as
|
||||
// the hop number and the encryption type).
|
||||
data.hop_no++;
|
||||
lmq_server_->request(
|
||||
sn.pubkey_x25519.view(), "sn.onion_request", std::move(cb),
|
||||
oxenmq::send_option::request_timeout{30s},
|
||||
lmq_server_.encode_onion_data(payload, data));
|
||||
}
|
||||
}
|
||||
|
||||
// Calls callback on success only?
|
||||
|
|
|
@ -31,6 +31,8 @@ inline constexpr int STORAGE_SERVER_HARDFORK = 17;
|
|||
inline constexpr int HARDFORK_SN_PING = 18;
|
||||
// HF at which we can stop using hex conversion for pubkey sorting for testee/testers.
|
||||
inline constexpr int HARDFORK_NO_HEX_SORT_HACK = 18;
|
||||
// HF at which we start using the sn.onion_request endpoint instead of sn.onion_req_v2
|
||||
inline constexpr int HARDFORK_OMQ_ONION_REQ_BENCODE = 18;
|
||||
|
||||
namespace storage {
|
||||
struct Item;
|
||||
|
@ -40,6 +42,8 @@ struct sn_response_t;
|
|||
|
||||
class OxenmqServer;
|
||||
|
||||
struct OnionRequestMetadata;
|
||||
|
||||
namespace ss_client {
|
||||
class Request;
|
||||
enum class ReqMethod;
|
||||
|
@ -196,16 +200,10 @@ class ServiceNode {
|
|||
void record_proxy_request();
|
||||
void record_onion_request();
|
||||
|
||||
// This is new, so it does not need to support http, thus new (if temp)
|
||||
// method
|
||||
void send_onion_to_sn_v1(const sn_record_t& sn, const std::string& payload,
|
||||
const std::string& eph_key,
|
||||
ss_client::Callback cb) const;
|
||||
|
||||
/// Same as v1, but using the new protocol (ciphertext as binary)
|
||||
void send_onion_to_sn_v2(const sn_record_t& sn, const std::string& payload,
|
||||
const std::string& eph_key,
|
||||
ss_client::Callback cb) const;
|
||||
/// Sends an onion request to the next SS
|
||||
void send_onion_to_sn(const sn_record_t& sn, std::string_view payload,
|
||||
OnionRequestMetadata&& data,
|
||||
ss_client::Callback cb) const;
|
||||
|
||||
// TODO: move this eventually out of SN
|
||||
// Send by either http or omq
|
||||
|
|
Loading…
Reference in a new issue