From 0632e88de0b2d85d869b28ec145f7f13f7dc98b9 Mon Sep 17 00:00:00 2001 From: dan Date: Thu, 26 Jan 2023 09:51:55 -0800 Subject: [PATCH] Make new header for json type conversions --- daemon/lokinet-vpn.cpp | 8 +-- llarp/CMakeLists.txt | 1 + llarp/net/ip_range.hpp | 14 ++++++ llarp/rpc/json_binary_proxy.hpp | 23 --------- llarp/rpc/json_conversions.cpp | 18 +++++++ llarp/rpc/json_conversions.hpp | 37 ++++++++++++++ llarp/rpc/param_parser.hpp | 66 ++++++++++++++----------- llarp/rpc/rpc_request.hpp | 1 - llarp/rpc/rpc_request_definitions.hpp | 70 ++------------------------- llarp/rpc/rpc_request_parser.cpp | 7 +-- llarp/rpc/rpc_server.cpp | 57 +++++++++------------- llarp/rpc/rpc_server.hpp | 4 -- 12 files changed, 140 insertions(+), 166 deletions(-) create mode 100644 llarp/rpc/json_conversions.cpp create mode 100644 llarp/rpc/json_conversions.hpp diff --git a/daemon/lokinet-vpn.cpp b/daemon/lokinet-vpn.cpp index 98d551f3a..26d2f47b2 100644 --- a/daemon/lokinet-vpn.cpp +++ b/daemon/lokinet-vpn.cpp @@ -225,7 +225,7 @@ main(int argc, char* argv[]) { nlohmann::json opts{{"address", options.exitAddress}, {"token", options.token}}; 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)); @@ -240,10 +240,10 @@ main(int argc, char* argv[]) } if (options.vpnDown) { - nlohmann::json opts{{"unmap", true}}; + nlohmann::json opts{{"unmap_exit", true}}; if (options.range) - opts["range"] = *options.range; - if (not OMQ_Request(omq, connectionID, "llarp.exit", std::move(opts))) + opts["ip_range"] = *options.range; + if (not OMQ_Request(omq, connectionID, "llarp.unmap_exit", std::move(opts))) return exit_error("failed to unmap exit"); } diff --git a/llarp/CMakeLists.txt b/llarp/CMakeLists.txt index 5c3ade29a..6653a5f08 100644 --- a/llarp/CMakeLists.txt +++ b/llarp/CMakeLists.txt @@ -247,6 +247,7 @@ add_library(lokinet-context add_library(lokinet-rpc STATIC rpc/json_binary_proxy.cpp + rpc/json_conversions.cpp rpc/lokid_rpc_client.cpp rpc/rpc_request_parser.cpp rpc/rpc_server.cpp diff --git a/llarp/net/ip_range.hpp b/llarp/net/ip_range.hpp index d0d494c37..d7045bb74 100644 --- a/llarp/net/ip_range.hpp +++ b/llarp/net/ip_range.hpp @@ -8,6 +8,7 @@ #include #include +#include #include namespace llarp @@ -24,6 +25,12 @@ namespace llarp : 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 StringInit(std::string _range) { @@ -32,6 +39,13 @@ namespace llarp return range; } + static bool + IsValidString(std::string _range) + { + IPRange range; + return (range.FromString(_range)); + } + static constexpr IPRange V4MappedRange() { diff --git a/llarp/rpc/json_binary_proxy.hpp b/llarp/rpc/json_binary_proxy.hpp index f0903a9b6..dc4800dfb 100644 --- a/llarp/rpc/json_binary_proxy.hpp +++ b/llarp/rpc/json_binary_proxy.hpp @@ -156,26 +156,3 @@ 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 - struct adl_serializer>> - { - static_assert(std::is_trivially_copyable_v && std::has_unique_object_representations_v); - - 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(), false /*no raw*/, val); - } - }; -} // namespace nlohmann diff --git a/llarp/rpc/json_conversions.cpp b/llarp/rpc/json_conversions.cpp new file mode 100644 index 000000000..d3e036468 --- /dev/null +++ b/llarp/rpc/json_conversions.cpp @@ -0,0 +1,18 @@ +#include "json_conversions.hpp" +#include + +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()}; + } + +} // namespace llarp diff --git a/llarp/rpc/json_conversions.hpp b/llarp/rpc/json_conversions.hpp new file mode 100644 index 000000000..95b9d547c --- /dev/null +++ b/llarp/rpc/json_conversions.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include +#include +#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 + struct adl_serializer>> + { + static_assert(std::is_trivially_copyable_v && std::has_unique_object_representations_v); + + 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(), false /*no raw*/, val); + } + }; + +} // namespace nlohmann diff --git a/llarp/rpc/param_parser.hpp b/llarp/rpc/param_parser.hpp index 744ac0b22..64779979c 100644 --- a/llarp/rpc/param_parser.hpp +++ b/llarp/rpc/param_parser.hpp @@ -1,14 +1,17 @@ #pragma once #include "json_binary_proxy.hpp" +#include "json_bt.hpp" +#include "json_conversions.hpp" #include #include +#include +#include #include #include namespace llarp::rpc { - using json_range = std::pair; using rpc_input = std::variant; @@ -116,6 +119,12 @@ namespace llarp::rpc template constexpr bool is_expandable_list> = true; + // Types that are constructible from string + template + constexpr bool is_string_constructible = false; + template <> + inline constexpr bool is_string_constructible = true; + // Fixed size elements: tuples, pairs, and std::array's; we accept list input as long as the // list length matches exactly. template @@ -147,32 +156,34 @@ namespace llarp::rpc oxenc::bt_dict_consumer> || std::is_same_v, int> = 0> void - load_value(BTConsumer& c, T& val) + load_value(BTConsumer& c, T& target) { if constexpr (std::is_integral_v) - val = c.template consume_integer(); + target = c.template consume_integer(); else if constexpr (std::is_same_v || std::is_same_v) - val = c.consume_string_view(); + target = c.consume_string_view(); + else if constexpr (is_string_constructible) + target = T{c.consume_string()}; else if constexpr (llarp::rpc::json_is_binary) - 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) { auto lc = c.consume_list_consumer(); - val.clear(); + target.clear(); while (!lc.is_finished()) - load_value(lc, val.emplace_back()); + load_value(lc, target.emplace_back()); } else if constexpr (is_tuple_like) { auto lc = c.consume_list_consumer(); - load_tuple_values(lc, val, std::make_index_sequence>{}); + load_tuple_values(lc, target, std::make_index_sequence>{}); } else if constexpr (is_unordered_string_map) { auto dc = c.consume_dict_consumer(); - val.clear(); + target.clear(); while (!dc.is_finished()) - load_value(dc, val[std::string{dc.key()}]); + load_value(dc, target[std::string{dc.key()}]); } else static_assert(std::is_same_v, "Unsupported load_value type"); @@ -182,21 +193,21 @@ namespace llarp::rpc // on unconvertible values. template void - load_value(json_range& r, T& val) + load_value(json_range& range_itr, T& target) { - auto& key = r.first.key(); - auto& e = *r.first; + auto& key = range_itr.first.key(); + auto& current = *range_itr.first; // value currently pointed to by range_itr.first if constexpr (std::is_same_v) { - if (e.is_boolean()) - val = e.get(); - else if (e.is_number_unsigned()) + if (current.is_boolean()) + target = current.get(); + else if (current.is_number_unsigned()) { // Also accept 0 or 1 for bools (mainly to be compatible with bt-encoding which doesn't // have a distinct bool type). - auto b = e.get(); + auto b = current.get(); if (b <= 1) - val = b; + target = b; else throw std::domain_error{"Invalid value for '" + key + "': expected boolean"}; } @@ -207,18 +218,18 @@ namespace llarp::rpc } else if constexpr (std::is_unsigned_v) { - if (!e.is_number_unsigned()) + if (!current.is_number_unsigned()) throw std::domain_error{"Invalid value for '" + key + "': non-negative value required"}; - auto i = e.get(); + auto i = current.get(); if (sizeof(T) < sizeof(uint64_t) && i > std::numeric_limits::max()) throw std::domain_error{"Invalid value for '" + key + "': value too large"}; - val = i; + target = i; } else if constexpr (std::is_integral_v) { - if (!e.is_number_integer()) + if (!current.is_number_integer()) throw std::domain_error{"Invalid value for '" + key + "': value is not an integer"}; - auto i = e.get(); + auto i = current.get(); if (sizeof(T) < sizeof(int64_t)) { if (i < std::numeric_limits::lowest()) @@ -227,11 +238,11 @@ namespace llarp::rpc if (i > std::numeric_limits::max()) throw std::domain_error{"Invalid value for '" + key + "': value is too large"}; } - val = i; + target = i; } else if constexpr (std::is_same_v || std::is_same_v) { - val = e.get(); + target = current.get(); } else if constexpr ( llarp::rpc::json_is_binary< @@ -239,7 +250,7 @@ namespace llarp::rpc { try { - e.get_to(val); + current.get_to(target); } catch (const std::exception& e) { @@ -250,7 +261,7 @@ namespace llarp::rpc { static_assert(std::is_same_v, "Unsupported load type"); } - ++r.first; + ++range_itr.first; } template @@ -355,5 +366,4 @@ namespace llarp::rpc } } } - } // namespace llarp::rpc \ No newline at end of file diff --git a/llarp/rpc/rpc_request.hpp b/llarp/rpc/rpc_request.hpp index 9c543aafe..847bb8856 100644 --- a/llarp/rpc/rpc_request.hpp +++ b/llarp/rpc/rpc_request.hpp @@ -14,7 +14,6 @@ namespace llarp::rpc { - using nlohmann::json; template diff --git a/llarp/rpc/rpc_request_definitions.hpp b/llarp/rpc/rpc_request_definitions.hpp index 0b7401840..cc40387a1 100644 --- a/llarp/rpc/rpc_request_definitions.hpp +++ b/llarp/rpc/rpc_request_definitions.hpp @@ -176,78 +176,16 @@ namespace llarp::rpc struct request_parameters { std::string address; - std::vector ip_range; + std::vector ip_range; std::string token; } 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 // List all currently mapped exit node connections // // Inputs: none - // + // // Returns: // struct ListExits : NoArgs @@ -271,15 +209,13 @@ namespace llarp::rpc struct request_parameters { - std::vector ip_range; + std::vector ip_range; } request; }; // RPC: dns_query // Attempts to query endpoint by domain name // - // Note: ask Jason about the internals of this - // // Inputs: // "endpoint" : endpoint ID to query (string) // "qname" : query name (string) diff --git a/llarp/rpc/rpc_request_parser.cpp b/llarp/rpc/rpc_request_parser.cpp index 1a0732d77..4d961df3c 100644 --- a/llarp/rpc/rpc_request_parser.cpp +++ b/llarp/rpc/rpc_request_parser.cpp @@ -58,7 +58,7 @@ namespace llarp::rpc input, "address", mapexit.request.address, - "IP_range", + "ip_range", mapexit.request.ip_range, "token", mapexit.request.token); @@ -67,10 +67,7 @@ namespace llarp::rpc void parse_request(UnmapExit& unmapexit, rpc_input input) { - get_values( - input, - "IP_range", - unmapexit.request.ip_range); + get_values(input, "ip_range", unmapexit.request.ip_range); } void diff --git a/llarp/rpc/rpc_server.cpp b/llarp/rpc/rpc_server.cpp index 40a4ae6af..e7254376e 100644 --- a/llarp/rpc/rpc_server.cpp +++ b/llarp/rpc/rpc_server.cpp @@ -91,11 +91,11 @@ namespace llarp::rpc register_rpc_command(std::unordered_map& regs) { static_assert(std::is_base_of_v); - auto cback = std::make_shared(); + rpc_callback cback{}; - cback->invoke = make_invoke(); + cback.invoke = make_invoke(); - regs.emplace(RPC::name, cback); + regs.emplace(RPC::name, std::move(cback)); } RPCServer::RPCServer(LMQ_ptr lmq, AbstractRouter& r) @@ -355,12 +355,17 @@ namespace llarp::rpc MapExit exit_request; // steal replier from exit RPC endpoint 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 @@ -369,8 +374,10 @@ namespace llarp::rpc if (not m_Router.hiddenServiceContext().hasEndpoints()) listexits.response = CreateJSONError("No mapped endpoints found"); else - listexits.response = CreateJSONResponse( - m_Router.hiddenServiceContext().GetDefault()->ExtractStatus()["m_ExitMap"]); + listexits.response = + CreateJSONResponse(m_Router.hiddenServiceContext().GetDefault()->ExtractStatus()["m_" + "ExitMa" + "p"]); } void @@ -382,22 +389,14 @@ namespace llarp::rpc return; } - std::vector range{}; - - for (auto& ip : unmapexit.request.ip_range) + try { - try { - range.push_back(IPRange::StringInit(ip)); - } catch (std::exception& e) { - unmapexit.response = CreateJSONError(e.what()); - } - } - - try { m_Router.routePoker()->Down(); - for (auto& ip : range) + for (auto& ip : unmapexit.request.ip_range) m_Router.hiddenServiceContext().GetDefault()->UnmapExitRange(ip); - } catch (std::exception& e) { + } + catch (std::exception& e) + { unmapexit.response = CreateJSONError("Unable to unmap to given range"); } @@ -439,16 +438,6 @@ namespace llarp::rpc 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 RPCServer::invoke(Config& config) { diff --git a/llarp/rpc/rpc_server.hpp b/llarp/rpc/rpc_server.hpp index dc07db30f..3d504673d 100644 --- a/llarp/rpc/rpc_server.hpp +++ b/llarp/rpc/rpc_server.hpp @@ -150,10 +150,6 @@ namespace llarp::rpc 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()) rpc.send_response(); }