Make new header for json type conversions

pull/2121/head
dan 1 year ago
parent 02b392881b
commit 0632e88de0

@ -225,7 +225,7 @@ main(int argc, char* argv[])
{ {
nlohmann::json opts{{"address", options.exitAddress}, {"token", options.token}}; nlohmann::json opts{{"address", options.exitAddress}, {"token", options.token}};
if (options.range) if (options.range)
opts["IP_range"] = *options.range; opts["ip_range"] = *options.range;
auto maybe_result = OMQ_Request(omq, connectionID, "llarp.map_exit", std::move(opts)); auto maybe_result = OMQ_Request(omq, connectionID, "llarp.map_exit", std::move(opts));
@ -240,10 +240,10 @@ main(int argc, char* argv[])
} }
if (options.vpnDown) if (options.vpnDown)
{ {
nlohmann::json opts{{"unmap", true}}; nlohmann::json opts{{"unmap_exit", true}};
if (options.range) if (options.range)
opts["range"] = *options.range; opts["ip_range"] = *options.range;
if (not OMQ_Request(omq, connectionID, "llarp.exit", std::move(opts))) 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");
} }

@ -247,6 +247,7 @@ add_library(lokinet-context
add_library(lokinet-rpc add_library(lokinet-rpc
STATIC STATIC
rpc/json_binary_proxy.cpp rpc/json_binary_proxy.cpp
rpc/json_conversions.cpp
rpc/lokid_rpc_client.cpp rpc/lokid_rpc_client.cpp
rpc/rpc_request_parser.cpp rpc/rpc_request_parser.cpp
rpc/rpc_server.cpp rpc/rpc_server.cpp

@ -8,6 +8,7 @@
#include <list> #include <list>
#include <optional> #include <optional>
#include <stdexcept>
#include <string> #include <string>
namespace llarp namespace llarp
@ -24,6 +25,12 @@ namespace llarp
: addr{std::move(address)}, netmask_bits{std::move(netmask)} : addr{std::move(address)}, netmask_bits{std::move(netmask)}
{} {}
explicit IPRange(std::string _range)
{
if (not FromString(_range))
throw std::invalid_argument{"IP string '{}' cannot be parsed as IP range"_format(_range)};
}
static IPRange static IPRange
StringInit(std::string _range) StringInit(std::string _range)
{ {
@ -32,6 +39,13 @@ namespace llarp
return range; return range;
} }
static bool
IsValidString(std::string _range)
{
IPRange range;
return (range.FromString(_range));
}
static constexpr IPRange static constexpr IPRange
V4MappedRange() V4MappedRange()
{ {

@ -156,26 +156,3 @@ namespace llarp::rpc
}; };
} // namespace llarp::rpc } // namespace llarp::rpc
// Specializations of binary types for deserialization; when receiving these from json we expect
// them encoded in hex or base64. These may *not* be used for serialization, and will throw if so
// invoked; for serialization you need to use RPC_COMMAND::response_hex (or _b64) instead.
namespace nlohmann
{
template <typename T>
struct adl_serializer<T, std::enable_if_t<llarp::rpc::json_is_binary<T>>>
{
static_assert(std::is_trivially_copyable_v<T> && std::has_unique_object_representations_v<T>);
static void
to_json(const T&)
{
throw std::logic_error{"Internal error: binary types are not directly serializable"};
}
static void
from_json(const json& j, T& val)
{
llarp::rpc::load_binary_parameter(j.get<std::string_view>(), false /*no raw*/, val);
}
};
} // namespace nlohmann

@ -0,0 +1,18 @@
#include "json_conversions.hpp"
#include <nlohmann/json.hpp>
namespace llarp
{
void
to_json(nlohmann::json& j, const IPRange& ipr)
{
j = ipr.ToString();
}
void
from_json(const nlohmann::json& j, IPRange& ipr)
{
ipr = IPRange{j.get<std::string>()};
}
} // namespace llarp

@ -0,0 +1,37 @@
#pragma once
#include <llarp/net/ip_range.hpp>
#include <nlohmann/json_fwd.hpp>
#include "json_binary_proxy.hpp"
namespace llarp
{
void
to_json(nlohmann::json& j, const IPRange& ipr);
void
from_json(const nlohmann::json& j, IPRange& ipr);
} // namespace llarp
namespace nlohmann
{
// Specializations of binary types for deserialization; when receiving these from json we expect
// them encoded in hex or base64. These may *not* be used for serialization, and will throw if so
// invoked; for serialization you need to use RPC_COMMAND::response_hex (or _b64) instead.
template <typename T>
struct adl_serializer<T, std::enable_if_t<llarp::rpc::json_is_binary<T>>>
{
static_assert(std::is_trivially_copyable_v<T> && std::has_unique_object_representations_v<T>);
static void
to_json(json&, const T&)
{
throw std::logic_error{"Internal error: binary types are not directly serializable"};
}
static void
from_json(const json& j, T& val)
{
llarp::rpc::load_binary_parameter(j.get<std::string_view>(), false /*no raw*/, val);
}
};
} // namespace nlohmann

@ -1,14 +1,17 @@
#pragma once #pragma once
#include "json_binary_proxy.hpp" #include "json_binary_proxy.hpp"
#include "json_bt.hpp"
#include "json_conversions.hpp"
#include <oxenc/bt_serialize.h> #include <oxenc/bt_serialize.h>
#include <nlohmann/json.hpp> #include <nlohmann/json.hpp>
#include <stdexcept>
#include <string>
#include <unordered_map> #include <unordered_map>
#include <optional> #include <optional>
namespace llarp::rpc namespace llarp::rpc
{ {
using json_range = std::pair<nlohmann::json::const_iterator, nlohmann::json::const_iterator>; using json_range = std::pair<nlohmann::json::const_iterator, nlohmann::json::const_iterator>;
using rpc_input = std::variant<std::monostate, nlohmann::json, oxenc::bt_dict_consumer>; using rpc_input = std::variant<std::monostate, nlohmann::json, oxenc::bt_dict_consumer>;
@ -116,6 +119,12 @@ namespace llarp::rpc
template <typename T> template <typename T>
constexpr bool is_expandable_list<std::vector<T>> = true; constexpr bool is_expandable_list<std::vector<T>> = true;
// Types that are constructible from string
template <typename T>
constexpr bool is_string_constructible = false;
template <>
inline constexpr bool is_string_constructible<IPRange> = true;
// Fixed size elements: tuples, pairs, and std::array's; we accept list input as long as the // Fixed size elements: tuples, pairs, and std::array's; we accept list input as long as the
// list length matches exactly. // list length matches exactly.
template <typename T> template <typename T>
@ -147,32 +156,34 @@ namespace llarp::rpc
oxenc::bt_dict_consumer> || std::is_same_v<BTConsumer, oxenc::bt_list_consumer>, oxenc::bt_dict_consumer> || std::is_same_v<BTConsumer, oxenc::bt_list_consumer>,
int> = 0> int> = 0>
void void
load_value(BTConsumer& c, T& val) load_value(BTConsumer& c, T& target)
{ {
if constexpr (std::is_integral_v<T>) if constexpr (std::is_integral_v<T>)
val = c.template consume_integer<T>(); target = c.template consume_integer<T>();
else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>) else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>)
val = c.consume_string_view(); target = c.consume_string_view();
else if constexpr (is_string_constructible<T>)
target = T{c.consume_string()};
else if constexpr (llarp::rpc::json_is_binary<T>) else if constexpr (llarp::rpc::json_is_binary<T>)
llarp::rpc::load_binary_parameter(c.consume_string_view(), true /*allow raw*/, val); llarp::rpc::load_binary_parameter(c.consume_string_view(), true /*allow raw*/, target);
else if constexpr (is_expandable_list<T>) else if constexpr (is_expandable_list<T>)
{ {
auto lc = c.consume_list_consumer(); auto lc = c.consume_list_consumer();
val.clear(); target.clear();
while (!lc.is_finished()) while (!lc.is_finished())
load_value(lc, val.emplace_back()); load_value(lc, target.emplace_back());
} }
else if constexpr (is_tuple_like<T>) else if constexpr (is_tuple_like<T>)
{ {
auto lc = c.consume_list_consumer(); auto lc = c.consume_list_consumer();
load_tuple_values(lc, val, std::make_index_sequence<std::tuple_size_v<T>>{}); load_tuple_values(lc, target, std::make_index_sequence<std::tuple_size_v<T>>{});
} }
else if constexpr (is_unordered_string_map<T>) else if constexpr (is_unordered_string_map<T>)
{ {
auto dc = c.consume_dict_consumer(); auto dc = c.consume_dict_consumer();
val.clear(); target.clear();
while (!dc.is_finished()) while (!dc.is_finished())
load_value(dc, val[std::string{dc.key()}]); load_value(dc, target[std::string{dc.key()}]);
} }
else else
static_assert(std::is_same_v<T, void>, "Unsupported load_value type"); static_assert(std::is_same_v<T, void>, "Unsupported load_value type");
@ -182,21 +193,21 @@ namespace llarp::rpc
// on unconvertible values. // on unconvertible values.
template <typename T> template <typename T>
void void
load_value(json_range& r, T& val) load_value(json_range& range_itr, T& target)
{ {
auto& key = r.first.key(); auto& key = range_itr.first.key();
auto& e = *r.first; auto& current = *range_itr.first; // value currently pointed to by range_itr.first
if constexpr (std::is_same_v<T, bool>) if constexpr (std::is_same_v<T, bool>)
{ {
if (e.is_boolean()) if (current.is_boolean())
val = e.get<bool>(); target = current.get<bool>();
else if (e.is_number_unsigned()) else if (current.is_number_unsigned())
{ {
// Also accept 0 or 1 for bools (mainly to be compatible with bt-encoding which doesn't // Also accept 0 or 1 for bools (mainly to be compatible with bt-encoding which doesn't
// have a distinct bool type). // have a distinct bool type).
auto b = e.get<uint64_t>(); auto b = current.get<uint64_t>();
if (b <= 1) if (b <= 1)
val = b; target = b;
else else
throw std::domain_error{"Invalid value for '" + key + "': expected boolean"}; throw std::domain_error{"Invalid value for '" + key + "': expected boolean"};
} }
@ -207,18 +218,18 @@ namespace llarp::rpc
} }
else if constexpr (std::is_unsigned_v<T>) else if constexpr (std::is_unsigned_v<T>)
{ {
if (!e.is_number_unsigned()) if (!current.is_number_unsigned())
throw std::domain_error{"Invalid value for '" + key + "': non-negative value required"}; throw std::domain_error{"Invalid value for '" + key + "': non-negative value required"};
auto i = e.get<uint64_t>(); auto i = current.get<uint64_t>();
if (sizeof(T) < sizeof(uint64_t) && i > std::numeric_limits<T>::max()) if (sizeof(T) < sizeof(uint64_t) && i > std::numeric_limits<T>::max())
throw std::domain_error{"Invalid value for '" + key + "': value too large"}; throw std::domain_error{"Invalid value for '" + key + "': value too large"};
val = i; target = i;
} }
else if constexpr (std::is_integral_v<T>) else if constexpr (std::is_integral_v<T>)
{ {
if (!e.is_number_integer()) if (!current.is_number_integer())
throw std::domain_error{"Invalid value for '" + key + "': value is not an integer"}; throw std::domain_error{"Invalid value for '" + key + "': value is not an integer"};
auto i = e.get<int64_t>(); auto i = current.get<int64_t>();
if (sizeof(T) < sizeof(int64_t)) if (sizeof(T) < sizeof(int64_t))
{ {
if (i < std::numeric_limits<T>::lowest()) if (i < std::numeric_limits<T>::lowest())
@ -227,11 +238,11 @@ namespace llarp::rpc
if (i > std::numeric_limits<T>::max()) if (i > std::numeric_limits<T>::max())
throw std::domain_error{"Invalid value for '" + key + "': value is too large"}; throw std::domain_error{"Invalid value for '" + key + "': value is too large"};
} }
val = i; target = i;
} }
else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>) else if constexpr (std::is_same_v<T, std::string> || std::is_same_v<T, std::string_view>)
{ {
val = e.get<std::string_view>(); target = current.get<std::string_view>();
} }
else if constexpr ( else if constexpr (
llarp::rpc::json_is_binary< llarp::rpc::json_is_binary<
@ -239,7 +250,7 @@ namespace llarp::rpc
{ {
try try
{ {
e.get_to(val); current.get_to(target);
} }
catch (const std::exception& e) catch (const std::exception& e)
{ {
@ -250,7 +261,7 @@ namespace llarp::rpc
{ {
static_assert(std::is_same_v<T, void>, "Unsupported load type"); static_assert(std::is_same_v<T, void>, "Unsupported load type");
} }
++r.first; ++range_itr.first;
} }
template <typename TupleLike, size_t... Is> template <typename TupleLike, size_t... Is>
@ -355,5 +366,4 @@ namespace llarp::rpc
} }
} }
} }
} // namespace llarp::rpc } // namespace llarp::rpc

@ -14,7 +14,6 @@
namespace llarp::rpc namespace llarp::rpc
{ {
using nlohmann::json; using nlohmann::json;
template <typename RPC> template <typename RPC>

@ -176,78 +176,16 @@ namespace llarp::rpc
struct request_parameters struct request_parameters
{ {
std::string address; std::string address;
std::vector<std::string> ip_range; std::vector<IPRange> ip_range;
std::string token; std::string token;
} request; } request;
void
onGoodResult(std::string reason, bool hasClient)
{
response = (hasClient) ? nlohmann::json{{"result", reason}}.dump()
: nlohmann::json{{"error", "We don't have an exit?"}}.dump();
}
void
onBadResult(
std::string reason, AbstractRouter& abs, llarp::service::Endpoint_ptr eptr, IPRange range)
{
abs.routePoker()->Down();
eptr->UnmapExitRange(range);
response = nlohmann::json{{"result", reason}}.dump();
}
void
mapExit(
service::Address addr,
AbstractRouter& router,
llarp::service::Endpoint_ptr eptr,
IPRange range,
service::Address exitAddr)
{
eptr->MapExitRange(range, addr);
bool sendAuth = (request.token.empty()) ? false : true;
if (sendAuth)
eptr->SetAuthInfoForEndpoint(exitAddr, service::AuthInfo{request.token});
if (addr.IsZero())
{
onGoodResult("Null exit added", router.HasClientExit());
return;
}
eptr->MarkAddressOutbound(addr);
eptr->EnsurePathToService(addr, [&](auto, service::OutboundContext* ctx) {
if (ctx == nullptr)
{
onBadResult("Could not find exit", router, eptr, range);
return;
}
if (not sendAuth)
{
onGoodResult("OK: connected to " + addr.ToString(), router.HasClientExit());
return;
}
// only lambda that we will keep
ctx->AsyncSendAuth([&](service::AuthResult result) {
if (result.code != service::AuthResultCode::eAuthAccepted)
{
onBadResult(result.reason, router, eptr, range);
return;
}
onGoodResult(result.reason, router.HasClientExit());
return;
});
});
}
}; };
// RPC: list_exits // RPC: list_exits
// List all currently mapped exit node connections // List all currently mapped exit node connections
// //
// Inputs: none // Inputs: none
// //
// Returns: // Returns:
// //
struct ListExits : NoArgs struct ListExits : NoArgs
@ -271,15 +209,13 @@ namespace llarp::rpc
struct request_parameters struct request_parameters
{ {
std::vector<std::string> ip_range; std::vector<IPRange> ip_range;
} request; } request;
}; };
// RPC: dns_query // RPC: dns_query
// Attempts to query endpoint by domain name // Attempts to query endpoint by domain name
// //
// Note: ask Jason about the internals of this
//
// Inputs: // Inputs:
// "endpoint" : endpoint ID to query (string) // "endpoint" : endpoint ID to query (string)
// "qname" : query name (string) // "qname" : query name (string)

@ -58,7 +58,7 @@ namespace llarp::rpc
input, input,
"address", "address",
mapexit.request.address, mapexit.request.address,
"IP_range", "ip_range",
mapexit.request.ip_range, mapexit.request.ip_range,
"token", "token",
mapexit.request.token); mapexit.request.token);
@ -67,10 +67,7 @@ namespace llarp::rpc
void void
parse_request(UnmapExit& unmapexit, rpc_input input) parse_request(UnmapExit& unmapexit, rpc_input input)
{ {
get_values( get_values(input, "ip_range", unmapexit.request.ip_range);
input,
"IP_range",
unmapexit.request.ip_range);
} }
void void

@ -91,11 +91,11 @@ namespace llarp::rpc
register_rpc_command(std::unordered_map<std::string, rpc_callback>& regs) register_rpc_command(std::unordered_map<std::string, rpc_callback>& regs)
{ {
static_assert(std::is_base_of_v<RPCRequest, RPC>); static_assert(std::is_base_of_v<RPCRequest, RPC>);
auto cback = std::make_shared<rpc_callback>(); rpc_callback cback{};
cback->invoke = make_invoke<RPC>(); cback.invoke = make_invoke<RPC>();
regs.emplace(RPC::name, cback); regs.emplace(RPC::name, std::move(cback));
} }
RPCServer::RPCServer(LMQ_ptr lmq, AbstractRouter& r) RPCServer::RPCServer(LMQ_ptr lmq, AbstractRouter& r)
@ -355,12 +355,17 @@ namespace llarp::rpc
MapExit exit_request; MapExit exit_request;
// steal replier from exit RPC endpoint // steal replier from exit RPC endpoint
exit_request.replier.emplace(std::move(*mapexit.replier)); exit_request.replier.emplace(std::move(*mapexit.replier));
// m_Router.hiddenServiceContext().GetDefault()->map_exit(
// mapexit.request.address,
// mapexit.request.token,
// mapexit.request.ip_range,
// [exit = std::move(exit_request)](bool success, std::string result) mutable {
if (success)
exit.send_response({{"result"}, std::move(result)});
else
exit.send_response({{"error"}, std::move(result)});
});
} }
void void
@ -369,8 +374,10 @@ namespace llarp::rpc
if (not m_Router.hiddenServiceContext().hasEndpoints()) if (not m_Router.hiddenServiceContext().hasEndpoints())
listexits.response = CreateJSONError("No mapped endpoints found"); listexits.response = CreateJSONError("No mapped endpoints found");
else else
listexits.response = CreateJSONResponse( listexits.response =
m_Router.hiddenServiceContext().GetDefault()->ExtractStatus()["m_ExitMap"]); CreateJSONResponse(m_Router.hiddenServiceContext().GetDefault()->ExtractStatus()["m_"
"ExitMa"
"p"]);
} }
void void
@ -382,22 +389,14 @@ namespace llarp::rpc
return; return;
} }
std::vector<IPRange> range{}; try
for (auto& ip : unmapexit.request.ip_range)
{ {
try {
range.push_back(IPRange::StringInit(ip));
} catch (std::exception& e) {
unmapexit.response = CreateJSONError(e.what());
}
}
try {
m_Router.routePoker()->Down(); m_Router.routePoker()->Down();
for (auto& ip : range) for (auto& ip : unmapexit.request.ip_range)
m_Router.hiddenServiceContext().GetDefault()->UnmapExitRange(ip); m_Router.hiddenServiceContext().GetDefault()->UnmapExitRange(ip);
} catch (std::exception& e) { }
catch (std::exception& e)
{
unmapexit.response = CreateJSONError("Unable to unmap to given range"); unmapexit.response = CreateJSONError("Unable to unmap to given range");
} }
@ -439,16 +438,6 @@ namespace llarp::rpc
return; return;
} }
/*
only have simple filename ex: "persist_key.ini"
create .ini files inside conf.d
add delete functionality
delete parameter (bool)
use same filename parameter
*/
void void
RPCServer::invoke(Config& config) RPCServer::invoke(Config& config)
{ {

@ -150,10 +150,6 @@ namespace llarp::rpc
fmt::format("RPC request 'rpc.{}' raised an exception: {}", rpc.name, e.what())); fmt::format("RPC request 'rpc.{}' raised an exception: {}", rpc.name, e.what()));
} }
// check if std::optional in rpc is present
// then rpc.send_response
// else
// do nothing because invoke stole RPC
if (rpc.replier.has_value()) if (rpc.replier.has_value())
rpc.send_response(); rpc.send_response();
} }

Loading…
Cancel
Save