mirror of
https://github.com/oxen-io/lokinet
synced 2023-12-14 06:53:00 +01:00
working new endpoints
- added hotswap functionality - map_exit and unmap_exit working
This commit is contained in:
parent
0632e88de0
commit
b2e8cde64b
|
@ -95,5 +95,5 @@ else:
|
|||
socket.close(linger=0)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
# ./lmq-rpc.py ipc://$HOME/.oxen/testnet/oxend.sock 'llarp.get_service_nodes' | jq
|
||||
# sample usage:
|
||||
# ./omq-rpc.py ipc://$HOME/.oxen/testnet/oxend.sock 'llarp.get_service_nodes' | jq
|
||||
|
|
|
@ -5,10 +5,12 @@
|
|||
#include <vector>
|
||||
#include <array>
|
||||
#include <llarp/net/net.hpp>
|
||||
#include <string_view>
|
||||
|
||||
#include <CLI/App.hpp>
|
||||
#include <CLI/Formatter.hpp>
|
||||
#include <CLI/Config.hpp>
|
||||
#include "oxenmq/address.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
// add the unholy windows headers for iphlpapi
|
||||
|
@ -56,7 +58,6 @@ OMQ_Request(
|
|||
|
||||
namespace
|
||||
{
|
||||
|
||||
struct command_line_options
|
||||
{
|
||||
// bool options
|
||||
|
@ -64,6 +65,7 @@ namespace
|
|||
bool help = false;
|
||||
bool vpnUp = false;
|
||||
bool vpnDown = false;
|
||||
bool swap = false;
|
||||
bool printStatus = false;
|
||||
bool killDaemon = false;
|
||||
|
||||
|
@ -73,9 +75,10 @@ namespace
|
|||
std::string endpoint = "default";
|
||||
std::string token;
|
||||
std::optional<std::string> range;
|
||||
std::vector<std::string> swapExits;
|
||||
|
||||
// oxenmq
|
||||
oxenmq::address rpcURL{"tcp://127.0.0.1:1190"};
|
||||
oxenmq::address rpcURL{};
|
||||
oxenmq::LogLevel logLevel = oxenmq::LogLevel::warn;
|
||||
};
|
||||
|
||||
|
@ -109,15 +112,23 @@ main(int argc, char* argv[])
|
|||
|
||||
// flags: boolean values in command_line_options struct
|
||||
cli.add_flag("-v,--verbose", options.verbose, "Verbose");
|
||||
cli.add_flag("--up", options.vpnUp, "Put VPN up");
|
||||
cli.add_flag("--down", options.vpnDown, "Put VPN down");
|
||||
cli.add_flag("--add,--up", options.vpnUp, "Map VPN connection to exit node [--up is deprecated]");
|
||||
cli.add_flag(
|
||||
"--remove,--down",
|
||||
options.vpnDown,
|
||||
"Unmap VPN connection to exit node [--down is deprecated]");
|
||||
cli.add_flag("--status", options.printStatus, "Print VPN status and exit");
|
||||
cli.add_flag("-k,--kill", options.killDaemon, "Kill lokinet daemon");
|
||||
|
||||
// options: string values in command_line_options struct
|
||||
cli.add_option("--exit", options.exitAddress, "Specify exit node address")->capture_default_str();
|
||||
cli.add_option("--endpoint", options.endpoint, "Endpoint to use")->capture_default_str();
|
||||
cli.add_option("--token", options.token, "Exit auth token to use")->capture_default_str();
|
||||
cli.add_option("--token,--auth", options.token, "Exit auth token to use")->capture_default_str();
|
||||
cli.add_option("--range", options.range, "IP range to map exit to")->capture_default_str();
|
||||
cli.add_option(
|
||||
"--swap", options.swapExits, "Exit addresses to swap mapped connection to [old] [new]")
|
||||
->expected(2)
|
||||
->capture_default_str();
|
||||
|
||||
// options: oxenmq values in command_line_options struct
|
||||
cli.add_option("--rpc", options.rpc, "Specify RPC URL for lokinet")->capture_default_str();
|
||||
|
@ -149,16 +160,17 @@ main(int argc, char* argv[])
|
|||
cli.exit(e);
|
||||
};
|
||||
|
||||
int numCommands = options.vpnUp + options.vpnDown + options.printStatus + options.killDaemon;
|
||||
int numCommands = options.vpnUp + options.vpnDown + options.printStatus + options.killDaemon
|
||||
+ (not options.swapExits.empty());
|
||||
|
||||
switch (numCommands)
|
||||
{
|
||||
case 0:
|
||||
return exit_error(3, "One of --up/--down/--status/--kill must be specified");
|
||||
return exit_error(3, "One of --add/--remove/--swap/--status/--kill must be specified");
|
||||
case 1:
|
||||
break;
|
||||
default:
|
||||
return exit_error(3, "Only one of --up/--down/--status/--kill may be specified");
|
||||
return exit_error(3, "Only one of --add/--remove/--swap/--status/--kill may be specified");
|
||||
}
|
||||
|
||||
if (options.vpnUp and options.exitAddress.empty())
|
||||
|
@ -170,12 +182,14 @@ main(int argc, char* argv[])
|
|||
},
|
||||
options.logLevel};
|
||||
|
||||
options.rpcURL = oxenmq::address{(options.rpc.empty()) ? "tcp://127.0.0.1:1190" : options.rpc};
|
||||
|
||||
omq.start();
|
||||
|
||||
std::promise<bool> connectPromise;
|
||||
|
||||
const auto connectionID = omq.connect_remote(
|
||||
options.rpc,
|
||||
options.rpcURL,
|
||||
[&connectPromise](auto) { connectPromise.set_value(true); },
|
||||
[&connectPromise](auto, std::string_view msg) {
|
||||
std::cout << "Failed to connect to lokinet RPC: " << msg << std::endl;
|
||||
|
@ -201,15 +215,15 @@ main(int argc, char* argv[])
|
|||
|
||||
try
|
||||
{
|
||||
const auto& ep = maybe_status->at("result").at("services").at(options.endpoint);
|
||||
const auto exitMap = ep.at("exitMap");
|
||||
if (exitMap.empty())
|
||||
const auto& ep = maybe_status->at("result").at("services").at(options.endpoint).at("exitMap");
|
||||
|
||||
if (ep.empty())
|
||||
{
|
||||
std::cout << "no exits" << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (const auto& [range, exit] : exitMap.items())
|
||||
for (const auto& [range, exit] : ep.items())
|
||||
{
|
||||
std::cout << range << " via " << exit.get<std::string>() << std::endl;
|
||||
}
|
||||
|
@ -221,6 +235,15 @@ main(int argc, char* argv[])
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (not options.swapExits.empty())
|
||||
{
|
||||
nlohmann::json opts{{"exit_addresses", std::move(options.swapExits)}};
|
||||
|
||||
if (not OMQ_Request(omq, connectionID, "llarp.swap_exits", std::move(opts)))
|
||||
return exit_error("Failed to swap exit node connections");
|
||||
}
|
||||
|
||||
if (options.vpnUp)
|
||||
{
|
||||
nlohmann::json opts{{"address", options.exitAddress}, {"token", options.token}};
|
||||
|
@ -244,7 +267,7 @@ main(int argc, char* argv[])
|
|||
if (options.range)
|
||||
opts["ip_range"] = *options.range;
|
||||
if (not OMQ_Request(omq, connectionID, "llarp.unmap_exit", std::move(opts)))
|
||||
return exit_error("failed to unmap exit");
|
||||
return exit_error("Failed to unmap exit node connection");
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -31,21 +31,6 @@ namespace llarp
|
|||
throw std::invalid_argument{"IP string '{}' cannot be parsed as IP range"_format(_range)};
|
||||
}
|
||||
|
||||
static IPRange
|
||||
StringInit(std::string _range)
|
||||
{
|
||||
IPRange range{};
|
||||
range.FromString(_range);
|
||||
return range;
|
||||
}
|
||||
|
||||
static bool
|
||||
IsValidString(std::string _range)
|
||||
{
|
||||
IPRange range;
|
||||
return (range.FromString(_range));
|
||||
}
|
||||
|
||||
static constexpr IPRange
|
||||
V4MappedRange()
|
||||
{
|
||||
|
@ -62,7 +47,8 @@ namespace llarp
|
|||
FromIPv4(net::ipv4addr_t addr, net::ipv4addr_t netmask)
|
||||
{
|
||||
return IPRange{
|
||||
net::ExpandV4(ToHost(addr)), netmask_ipv6_bits(bits::count_bits(netmask) + 96)};
|
||||
net::ExpandV4(llarp::net::ToHost(addr)),
|
||||
netmask_ipv6_bits(bits::count_bits(netmask) + 96)};
|
||||
}
|
||||
|
||||
/// return true if this iprange is in the IPv4 mapping range for containing ipv4 addresses
|
||||
|
@ -125,7 +111,7 @@ namespace llarp
|
|||
inline bool
|
||||
Contains(const net::ipaddr_t& ip) const
|
||||
{
|
||||
return var::visit([this](auto&& ip) { return Contains(ToHost(ip)); }, ip);
|
||||
return var::visit([this](auto&& ip) { return Contains(llarp::net::ToHost(ip)); }, ip);
|
||||
}
|
||||
|
||||
/// get the highest address on this range
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "ip_range.hpp"
|
||||
#include <llarp/util/status.hpp>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
namespace llarp
|
||||
|
|
|
@ -25,10 +25,14 @@ namespace llarp::rpc
|
|||
auto& rpc = handler.rpc;
|
||||
|
||||
if (m.data.size() > 1)
|
||||
m.send_reply(CreateJSONError(
|
||||
{
|
||||
m.send_reply(nlohmann::json{
|
||||
{"error",
|
||||
"Bad Request: RPC requests must have at most one data part (received {})"_format(
|
||||
m.data.size())));
|
||||
|
||||
m.data.size())}}
|
||||
.dump());
|
||||
return;
|
||||
}
|
||||
// parsing input as bt or json
|
||||
// hand off to parse_request (overloaded versions)
|
||||
try
|
||||
|
@ -49,7 +53,7 @@ namespace llarp::rpc
|
|||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
m.send_reply(CreateJSONError("Failed to parse request parameters: "s + e.what()));
|
||||
m.send_reply(nlohmann::json{{"Failed to parse request parameters: "s + e.what()}}.dump());
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -95,12 +95,23 @@ namespace llarp::rpc
|
|||
llarp::rpc::json_binary_proxy response_b64{
|
||||
response, llarp::rpc::json_binary_proxy::fmt::base64};
|
||||
|
||||
// The oxenmq deferred send object into which the response will be set. If this optional is
|
||||
// still set when the `invoke` call returns then the response is sent at that point; if it has
|
||||
// been moved out (i.e. either just this instance or the whole request struct is stolen/moved
|
||||
// by the invoke function) then it is the invoke function's job to send a reply. Typically
|
||||
// this is done when a response cannot be sent immediately
|
||||
// The oxenmq deferred send object into which the response will be sent when the `invoke`
|
||||
// method returns. If the response needs to happen later (i.e. not immediately after `invoke`
|
||||
// returns) then you should call `defer()` to extract and clear this and then send the response
|
||||
// via the returned DeferredSend object yourself.
|
||||
std::optional<oxenmq::Message::DeferredSend> replier;
|
||||
|
||||
// Called to clear the current replier and return it. After this call the automatic reply will
|
||||
// not be generated; the caller is responsible for calling `->reply` on the returned optional
|
||||
// itself. This is typically used where a call has to be deferred, for example because it
|
||||
// depends on some network response to build the reply.
|
||||
oxenmq::Message::DeferredSend
|
||||
move()
|
||||
{
|
||||
auto r{std::move(*replier)};
|
||||
replier.reset();
|
||||
return r;
|
||||
}
|
||||
};
|
||||
|
||||
// Tag types that are inherited to set RPC endpoint properties
|
||||
|
|
|
@ -110,7 +110,7 @@ namespace llarp::rpc
|
|||
} request;
|
||||
};
|
||||
|
||||
// RPC: quick_listener
|
||||
// RPC: quic_listener
|
||||
// Connects to QUIC interface on local endpoint
|
||||
// Passes request parameters in nlohmann::json format
|
||||
//
|
||||
|
@ -171,6 +171,14 @@ namespace llarp::rpc
|
|||
//
|
||||
struct MapExit : RPCRequest
|
||||
{
|
||||
MapExit()
|
||||
{
|
||||
if constexpr (platform::supports_ipv6)
|
||||
request.ip_range.emplace_back("::/0");
|
||||
else
|
||||
request.ip_range.emplace_back("0.0.0.0/0");
|
||||
}
|
||||
|
||||
static constexpr auto name = "map_exit"sv;
|
||||
|
||||
struct request_parameters
|
||||
|
@ -205,6 +213,14 @@ namespace llarp::rpc
|
|||
//
|
||||
struct UnmapExit : RPCRequest
|
||||
{
|
||||
UnmapExit()
|
||||
{
|
||||
if constexpr (platform::supports_ipv6)
|
||||
request.ip_range.emplace_back("::/0");
|
||||
else
|
||||
request.ip_range.emplace_back("0.0.0.0/0");
|
||||
}
|
||||
|
||||
static constexpr auto name = "unmap_exit"sv;
|
||||
|
||||
struct request_parameters
|
||||
|
@ -213,6 +229,25 @@ namespace llarp::rpc
|
|||
} request;
|
||||
};
|
||||
|
||||
// RPC: swap_exit
|
||||
// Swap a connection from one exit to another
|
||||
//
|
||||
// Inputs:
|
||||
// "exits" : exit nodes to swap mappings from (index 0 = old exit, index 1 = new exit)
|
||||
//
|
||||
// Returns:
|
||||
//
|
||||
struct SwapExits : RPCRequest
|
||||
{
|
||||
static constexpr auto name = "swap_exits"sv;
|
||||
|
||||
struct request_parameters
|
||||
{
|
||||
std::vector<std::string> exit_addresses;
|
||||
std::string token;
|
||||
} request;
|
||||
};
|
||||
|
||||
// RPC: dns_query
|
||||
// Attempts to query endpoint by domain name
|
||||
//
|
||||
|
@ -268,6 +303,7 @@ namespace llarp::rpc
|
|||
LookupSnode,
|
||||
MapExit,
|
||||
ListExits,
|
||||
SwapExits,
|
||||
UnmapExit,
|
||||
DNSQuery,
|
||||
Config>;
|
||||
|
|
|
@ -70,6 +70,17 @@ namespace llarp::rpc
|
|||
get_values(input, "ip_range", unmapexit.request.ip_range);
|
||||
}
|
||||
|
||||
void
|
||||
parse_request(SwapExits& swapexits, rpc_input input)
|
||||
{
|
||||
get_values(
|
||||
input,
|
||||
"exit_addresses",
|
||||
swapexits.request.exit_addresses,
|
||||
"token",
|
||||
swapexits.request.token);
|
||||
}
|
||||
|
||||
void
|
||||
parse_request(DNSQuery& dnsquery, rpc_input input)
|
||||
{
|
||||
|
|
|
@ -26,6 +26,8 @@ namespace llarp::rpc
|
|||
void
|
||||
parse_request(UnmapExit& unmapexit, rpc_input input);
|
||||
void
|
||||
parse_request(SwapExits& swapexits, rpc_input input);
|
||||
void
|
||||
parse_request(DNSQuery& dnsquery, rpc_input input);
|
||||
void
|
||||
parse_request(Config& config, rpc_input input);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include "llarp/rpc/rpc_request_definitions.hpp"
|
||||
#include "rpc_request.hpp"
|
||||
#include "llarp/service/address.hpp"
|
||||
#include <cmath>
|
||||
#include <exception>
|
||||
#include <llarp/router/route_poker.hpp>
|
||||
#include <llarp/config/config.hpp>
|
||||
|
@ -147,10 +148,10 @@ namespace llarp::rpc
|
|||
{
|
||||
if (not m_Router.IsRunning())
|
||||
{
|
||||
halt.response = CreateJSONError("Router is not running");
|
||||
SetJSONError("Router is not running", halt.response);
|
||||
return;
|
||||
}
|
||||
halt.response = CreateJSONResponse("OK");
|
||||
SetJSONResponse("OK", halt.response);
|
||||
m_Router.Stop();
|
||||
}
|
||||
|
||||
|
@ -160,20 +161,20 @@ namespace llarp::rpc
|
|||
util::StatusObject result{
|
||||
{"version", llarp::VERSION_FULL}, {"uptime", to_json(m_Router.Uptime())}};
|
||||
|
||||
version.response = CreateJSONResponse(result);
|
||||
SetJSONResponse(result, version.response);
|
||||
}
|
||||
|
||||
void
|
||||
RPCServer::invoke(Status& status)
|
||||
{
|
||||
status.response = (m_Router.IsRunning()) ? CreateJSONResponse(m_Router.ExtractStatus())
|
||||
: CreateJSONError("Router is not yet ready");
|
||||
(m_Router.IsRunning()) ? SetJSONResponse(m_Router.ExtractStatus(), status.response)
|
||||
: SetJSONError("Router is not yet ready", status.response);
|
||||
}
|
||||
|
||||
void
|
||||
RPCServer::invoke(GetStatus& getstatus)
|
||||
{
|
||||
getstatus.response = CreateJSONResponse(m_Router.ExtractSummaryStatus());
|
||||
SetJSONResponse(m_Router.ExtractSummaryStatus(), getstatus.response);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -181,13 +182,13 @@ namespace llarp::rpc
|
|||
{
|
||||
if (quicconnect.request.port == 0 and quicconnect.request.closeID == 0)
|
||||
{
|
||||
quicconnect.response = CreateJSONError("Port not provided");
|
||||
SetJSONError("Port not provided", quicconnect.response);
|
||||
return;
|
||||
}
|
||||
|
||||
if (quicconnect.request.remoteHost.empty() and quicconnect.request.closeID == 0)
|
||||
{
|
||||
quicconnect.response = CreateJSONError("Host not provided");
|
||||
SetJSONError("Host not provided", quicconnect.response);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -197,7 +198,7 @@ namespace llarp::rpc
|
|||
|
||||
if (not endpoint)
|
||||
{
|
||||
quicconnect.response = CreateJSONError("No such local endpoint found.");
|
||||
SetJSONError("No such local endpoint found.", quicconnect.response);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -205,15 +206,16 @@ namespace llarp::rpc
|
|||
|
||||
if (not quic)
|
||||
{
|
||||
quicconnect.response = CreateJSONError(
|
||||
"No quic interface available on endpoint " + quicconnect.request.endpoint);
|
||||
SetJSONError(
|
||||
"No quic interface available on endpoint " + quicconnect.request.endpoint,
|
||||
quicconnect.response);
|
||||
return;
|
||||
}
|
||||
|
||||
if (quicconnect.request.closeID)
|
||||
{
|
||||
quic->forget(quicconnect.request.closeID);
|
||||
quicconnect.response = CreateJSONResponse("OK");
|
||||
SetJSONResponse("OK", quicconnect.response);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -228,11 +230,11 @@ namespace llarp::rpc
|
|||
status["addr"] = addr.ToString();
|
||||
status["id"] = id;
|
||||
|
||||
quicconnect.response = CreateJSONResponse(status);
|
||||
SetJSONResponse(status, quicconnect.response);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
quicconnect.response = CreateJSONError(e.what());
|
||||
SetJSONError(e.what(), quicconnect.response);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -241,7 +243,7 @@ namespace llarp::rpc
|
|||
{
|
||||
if (quiclistener.request.port == 0 and quiclistener.request.closeID == 0)
|
||||
{
|
||||
quiclistener.response = CreateJSONError("Invalid arguments");
|
||||
SetJSONError("Invalid arguments", quiclistener.response);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -251,7 +253,7 @@ namespace llarp::rpc
|
|||
|
||||
if (not endpoint)
|
||||
{
|
||||
quiclistener.response = CreateJSONError("No such local endpoint found");
|
||||
SetJSONError("No such local endpoint found", quiclistener.response);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -259,15 +261,16 @@ namespace llarp::rpc
|
|||
|
||||
if (not quic)
|
||||
{
|
||||
quiclistener.response = CreateJSONError(
|
||||
"No quic interface available on endpoint " + quiclistener.request.endpoint);
|
||||
SetJSONError(
|
||||
"No quic interface available on endpoint " + quiclistener.request.endpoint,
|
||||
quiclistener.response);
|
||||
return;
|
||||
}
|
||||
|
||||
if (quiclistener.request.closeID)
|
||||
{
|
||||
quic->forget(quiclistener.request.closeID);
|
||||
quiclistener.response = CreateJSONResponse("OK");
|
||||
SetJSONResponse("OK", quiclistener.response);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -281,7 +284,7 @@ namespace llarp::rpc
|
|||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
quiclistener.response = CreateJSONError(e.what());
|
||||
SetJSONError(e.what(), quiclistener.response);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -298,30 +301,31 @@ namespace llarp::rpc
|
|||
endpoint->PutSRVRecord(std::move(srvData));
|
||||
}
|
||||
|
||||
quiclistener.response = CreateJSONResponse(result);
|
||||
SetJSONResponse(result, quiclistener.response);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: fix this because it's bad
|
||||
void
|
||||
RPCServer::invoke(LookupSnode& lookupsnode)
|
||||
{
|
||||
if (not m_Router.IsServiceNode())
|
||||
{
|
||||
lookupsnode.response = CreateJSONError("Not supported");
|
||||
SetJSONError("Not supported", lookupsnode.response);
|
||||
return;
|
||||
}
|
||||
|
||||
RouterID routerID;
|
||||
if (lookupsnode.request.routerID.empty())
|
||||
{
|
||||
lookupsnode.response = CreateJSONError("No remote ID provided");
|
||||
SetJSONError("No remote ID provided", lookupsnode.response);
|
||||
return;
|
||||
}
|
||||
|
||||
if (not routerID.FromString(lookupsnode.request.routerID))
|
||||
{
|
||||
lookupsnode.response = CreateJSONError("Invalid remote: " + lookupsnode.request.routerID);
|
||||
SetJSONError("Invalid remote: " + lookupsnode.request.routerID, lookupsnode.response);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -330,7 +334,7 @@ namespace llarp::rpc
|
|||
|
||||
if (endpoint == nullptr)
|
||||
{
|
||||
lookupsnode.response = CreateJSONError("Cannot find local endpoint: default");
|
||||
SetJSONError("Cannot find local endpoint: default", lookupsnode.response);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -339,11 +343,11 @@ namespace llarp::rpc
|
|||
{
|
||||
const auto ip = net::TruncateV6(endpoint->GetIPForIdent(PubKey{routerID}));
|
||||
util::StatusObject status{{"ip", ip.ToString()}};
|
||||
lookupsnode.response = CreateJSONResponse(status);
|
||||
SetJSONResponse(status, lookupsnode.response);
|
||||
return;
|
||||
}
|
||||
|
||||
lookupsnode.response = CreateJSONError("Failed to obtain snode session");
|
||||
SetJSONError("Failed to obtain snode session", lookupsnode.response);
|
||||
return;
|
||||
});
|
||||
});
|
||||
|
@ -354,7 +358,7 @@ namespace llarp::rpc
|
|||
{
|
||||
MapExit exit_request;
|
||||
// steal replier from exit RPC endpoint
|
||||
exit_request.replier.emplace(std::move(*mapexit.replier));
|
||||
exit_request.replier.emplace(mapexit.move());
|
||||
|
||||
m_Router.hiddenServiceContext().GetDefault()->map_exit(
|
||||
mapexit.request.address,
|
||||
|
@ -372,35 +376,104 @@ namespace llarp::rpc
|
|||
RPCServer::invoke(ListExits& listexits)
|
||||
{
|
||||
if (not m_Router.hiddenServiceContext().hasEndpoints())
|
||||
listexits.response = CreateJSONError("No mapped endpoints found");
|
||||
else
|
||||
listexits.response =
|
||||
CreateJSONResponse(m_Router.hiddenServiceContext().GetDefault()->ExtractStatus()["m_"
|
||||
"ExitMa"
|
||||
"p"]);
|
||||
{
|
||||
SetJSONError("No mapped endpoints found", listexits.response);
|
||||
return;
|
||||
}
|
||||
|
||||
auto status = m_Router.hiddenServiceContext().GetDefault()->ExtractStatus()["exitMap"];
|
||||
|
||||
SetJSONResponse((status.empty()) ? "No exits" : status, listexits.response);
|
||||
}
|
||||
|
||||
void
|
||||
RPCServer::invoke(UnmapExit& unmapexit)
|
||||
{
|
||||
if (unmapexit.request.ip_range.empty())
|
||||
{
|
||||
unmapexit.response = CreateJSONError("No IP range provided");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
m_Router.routePoker()->Down();
|
||||
for (auto& ip : unmapexit.request.ip_range)
|
||||
m_Router.hiddenServiceContext().GetDefault()->UnmapExitRange(ip);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
unmapexit.response = CreateJSONError("Unable to unmap to given range");
|
||||
SetJSONError("Unable to unmap to given range", unmapexit.response);
|
||||
return;
|
||||
}
|
||||
|
||||
unmapexit.response = CreateJSONResponse("OK");
|
||||
SetJSONResponse("OK", unmapexit.response);
|
||||
}
|
||||
|
||||
// Sequentially calls map_exit and unmap_exit to hotswap mapped connection from old exit
|
||||
// to new exit. Similar to how map_exit steals the oxenmq deferredsend object, swapexit
|
||||
// moves the replier object to the unmap_exit struct, as that is called second. Rather than
|
||||
// the nested lambda within map_exit making the reply call, it instead calls the unmap_exit logic
|
||||
// and leaves the message handling to the unmap_exit struct
|
||||
void
|
||||
RPCServer::invoke(SwapExits& swapexits)
|
||||
{
|
||||
MapExit map_request;
|
||||
UnmapExit unmap_request;
|
||||
auto endpoint = m_Router.hiddenServiceContext().GetDefault();
|
||||
auto current_exits = endpoint->ExtractStatus()["exitMap"];
|
||||
|
||||
if (current_exits.empty())
|
||||
{
|
||||
SetJSONError("Cannot swap to new exit: no exits currently mapped", swapexits.response);
|
||||
return;
|
||||
}
|
||||
|
||||
// steal replier from swapexit RPC endpoint
|
||||
unmap_request.replier.emplace(swapexits.move());
|
||||
|
||||
// set map_exit request to new address
|
||||
map_request.request.address = swapexits.request.exit_addresses[1];
|
||||
|
||||
// set token for new exit node mapping
|
||||
if (not swapexits.request.token.empty())
|
||||
map_request.request.token = swapexits.request.token;
|
||||
|
||||
// populate map_exit request with old IP ranges
|
||||
for (auto& [range, exit] : current_exits.items())
|
||||
{
|
||||
if (exit.get<std::string>() == swapexits.request.exit_addresses[0])
|
||||
{
|
||||
map_request.request.ip_range.emplace_back(range);
|
||||
unmap_request.request.ip_range.emplace_back(range);
|
||||
}
|
||||
}
|
||||
|
||||
if (map_request.request.ip_range.empty() or unmap_request.request.ip_range.empty())
|
||||
{
|
||||
SetJSONError("No mapped ranges found matching requested swap", swapexits.response);
|
||||
return;
|
||||
}
|
||||
|
||||
endpoint->map_exit(
|
||||
map_request.request.address,
|
||||
map_request.request.token,
|
||||
map_request.request.ip_range,
|
||||
[unmap = std::move(unmap_request),
|
||||
ep = endpoint,
|
||||
old_exit = swapexits.request.exit_addresses[0]](bool success, std::string result) mutable {
|
||||
if (not success)
|
||||
unmap.send_response({{"error"}, std::move(result)});
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
for (auto& ip : unmap.request.ip_range)
|
||||
ep->UnmapRangeByExit(ip, old_exit);
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
SetJSONError("Unable to unmap to given range", unmap.response);
|
||||
return;
|
||||
}
|
||||
|
||||
SetJSONResponse("OK", unmap.response);
|
||||
unmap.send_response();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -417,7 +490,7 @@ namespace llarp::rpc
|
|||
|
||||
if (endpoint == nullptr)
|
||||
{
|
||||
dnsquery.response = CreateJSONError("No such endpoint found for dns query");
|
||||
SetJSONError("No such endpoint found for dns query", dnsquery.response);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -425,16 +498,16 @@ namespace llarp::rpc
|
|||
{
|
||||
auto packet_src = std::make_shared<DummyPacketSource>([&](auto result) {
|
||||
if (result)
|
||||
dnsquery.response = CreateJSONResponse(result->ToJSON());
|
||||
SetJSONResponse(result->ToJSON(), dnsquery.response);
|
||||
else
|
||||
dnsquery.response = CreateJSONError("No response from DNS");
|
||||
SetJSONError("No response from DNS", dnsquery.response);
|
||||
});
|
||||
if (not dns->MaybeHandlePacket(
|
||||
packet_src, packet_src->dumb, packet_src->dumb, msg.ToBuffer()))
|
||||
dnsquery.response = CreateJSONError("DNS query not accepted by endpoint");
|
||||
SetJSONError("DNS query not accepted by endpoint", dnsquery.response);
|
||||
}
|
||||
else
|
||||
dnsquery.response = CreateJSONError("Endpoint does not have dns");
|
||||
SetJSONError("Endpoint does not have dns", dnsquery.response);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -443,24 +516,24 @@ namespace llarp::rpc
|
|||
{
|
||||
if (config.request.filename.empty() and not config.request.ini.empty())
|
||||
{
|
||||
config.response = CreateJSONError("No filename specified for .ini file");
|
||||
SetJSONError("No filename specified for .ini file", config.response);
|
||||
return;
|
||||
}
|
||||
if (config.request.ini.empty() and not config.request.filename.empty())
|
||||
{
|
||||
config.response = CreateJSONError("No .ini chunk provided");
|
||||
SetJSONError("No .ini chunk provided", config.response);
|
||||
return;
|
||||
}
|
||||
|
||||
if (not ends_with(config.request.filename, ".ini"))
|
||||
{
|
||||
config.response = CreateJSONError("Must append '.ini' to filename");
|
||||
SetJSONError("Must append '.ini' to filename", config.response);
|
||||
return;
|
||||
}
|
||||
|
||||
if (not check_path(config.request.filename))
|
||||
{
|
||||
config.response = CreateJSONError("Bad filename passed");
|
||||
SetJSONError("Bad filename passed", config.response);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -475,7 +548,7 @@ namespace llarp::rpc
|
|||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
config.response = CreateJSONError(e.what());
|
||||
SetJSONError(e.what(), config.response);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -496,12 +569,12 @@ namespace llarp::rpc
|
|||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
config.response = CreateJSONError(e.what());
|
||||
SetJSONError(e.what(), config.response);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
config.response = CreateJSONResponse("OK");
|
||||
SetJSONResponse("OK", config.response);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include "rpc_request_definitions.hpp"
|
||||
#include "json_bt.hpp"
|
||||
#include <nlohmann/json_fwd.hpp>
|
||||
#include <stdexcept>
|
||||
#include <string_view>
|
||||
#include <llarp/config/config.hpp>
|
||||
#include <oxenmq/oxenmq.h>
|
||||
|
@ -64,16 +66,16 @@ namespace llarp::rpc
|
|||
};
|
||||
|
||||
template <typename Result_t>
|
||||
std::string
|
||||
CreateJSONResponse(Result_t result)
|
||||
void
|
||||
SetJSONResponse(Result_t result, json& j)
|
||||
{
|
||||
return nlohmann::json{{"result", result}}.dump();
|
||||
j["result"] = result;
|
||||
}
|
||||
|
||||
inline std::string
|
||||
CreateJSONError(std::string_view msg)
|
||||
inline void
|
||||
SetJSONError(std::string_view msg, json& j)
|
||||
{
|
||||
return nlohmann::json{{"error", msg}}.dump();
|
||||
j["error"] = msg;
|
||||
}
|
||||
|
||||
class RPCServer
|
||||
|
@ -109,6 +111,8 @@ namespace llarp::rpc
|
|||
void
|
||||
invoke(UnmapExit& unmapexit);
|
||||
void
|
||||
invoke(SwapExits& swapexits);
|
||||
void
|
||||
invoke(DNSQuery& dnsquery);
|
||||
void
|
||||
invoke(Config& config);
|
||||
|
@ -140,19 +144,22 @@ namespace llarp::rpc
|
|||
catch (const rpc_error& e)
|
||||
{
|
||||
log::info(logcat, "RPC request 'rpc.{}' failed with: {}", rpc.name, e.what());
|
||||
rpc.response = CreateJSONError(
|
||||
fmt::format("RPC request 'rpc.{}' failed with: {}", rpc.name, e.what()));
|
||||
SetJSONError(
|
||||
fmt::format("RPC request 'rpc.{}' failed with: {}", rpc.name, e.what()), rpc.response);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
log::info(logcat, "RPC request 'rpc.{}' raised an exception: {}", rpc.name, e.what());
|
||||
rpc.response = CreateJSONError(
|
||||
fmt::format("RPC request 'rpc.{}' raised an exception: {}", rpc.name, e.what()));
|
||||
SetJSONError(
|
||||
fmt::format("RPC request 'rpc.{}' raised an exception: {}", rpc.name, e.what()),
|
||||
rpc.response);
|
||||
}
|
||||
|
||||
if (rpc.replier.has_value())
|
||||
{
|
||||
rpc.send_response();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace llarp::rpc
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include "endpoint_util.hpp"
|
||||
#include "hidden_service_address_lookup.hpp"
|
||||
#include "auth.hpp"
|
||||
#include "llarp/util/logging.hpp"
|
||||
#include "outbound_context.hpp"
|
||||
#include "protocol.hpp"
|
||||
#include "info.hpp"
|
||||
|
@ -2188,6 +2189,26 @@ namespace llarp
|
|||
LogInfo(Name(), " unmap ", item.first, " exit range mapping");
|
||||
return true;
|
||||
});
|
||||
|
||||
if (m_ExitMap.Empty())
|
||||
m_router->routePoker()->Down();
|
||||
}
|
||||
|
||||
void
|
||||
Endpoint::UnmapRangeByExit(IPRange range, std::string exit)
|
||||
{
|
||||
// unmap all ranges that match the given exit when hot swapping
|
||||
m_ExitMap.RemoveIf([&](const auto& item) -> bool {
|
||||
if ((range.Contains(item.first)) and (item.second.ToString() == exit))
|
||||
{
|
||||
log::info(logcat, "{} unmap {} range mapping to exit node {}", Name(), item.first, exit);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
if (m_ExitMap.Empty())
|
||||
m_router->routePoker()->Down();
|
||||
}
|
||||
|
||||
std::optional<AuthInfo>
|
||||
|
|
|
@ -284,6 +284,9 @@ namespace llarp
|
|||
void
|
||||
UnmapExitRange(IPRange range);
|
||||
|
||||
void
|
||||
UnmapRangeByExit(IPRange range, std::string exit);
|
||||
|
||||
void
|
||||
map_exit(
|
||||
std::string name,
|
||||
|
|
Loading…
Reference in a new issue