From 5f8e1ada154262d6b9b2a43cf80d862dcc27e9f2 Mon Sep 17 00:00:00 2001 From: dr7ana Date: Thu, 1 Feb 2024 04:43:43 -0800 Subject: [PATCH] formatting --- .clang-format | 47 +- daemon/lokinet-bootstrap.cpp | 131 +- daemon/lokinet-vpn.cpp | 463 +-- daemon/lokinet.cpp | 1076 ++++--- external/oxen-libquic | 2 +- include/llarp.hpp | 184 +- include/lokinet/lokinet_addr.h | 7 +- include/lokinet/lokinet_context.h | 69 +- include/lokinet/lokinet_misc.h | 67 +- include/lokinet/lokinet_socket.h | 14 +- include/lokinet/lokinet_srv.h | 99 +- include/lokinet/lokinet_stream.h | 88 +- include/lokinet/lokinet_udp.h | 196 +- jni/lokinet_config.cpp | 105 +- jni/lokinet_daemon.cpp | 189 +- jni/lokinet_jni_common.hpp | 70 +- jni/network_loki_lokinet_LokinetConfig.h | 68 +- jni/network_loki_lokinet_LokinetDaemon.h | 132 +- jni/network_loki_lokinet_LokinetVPN.h | 92 +- jni/network_loki_lokinet_Lokinet_JNI.h | 77 +- llarp/android/ifaddrs.c | 1077 ++++--- llarp/android/ifaddrs.h | 20 +- llarp/apple/DNSTrampoline.h | 35 +- llarp/apple/DNSTrampoline.m | 236 +- llarp/apple/PacketTunnelProvider.m | 572 ++-- llarp/apple/context.hpp | 29 +- llarp/apple/context_wrapper.cpp | 302 +- llarp/apple/context_wrapper.h | 290 +- llarp/apple/route_manager.cpp | 156 +- llarp/apple/route_manager.hpp | 71 +- llarp/apple/vpn_interface.cpp | 95 +- llarp/apple/vpn_interface.hpp | 61 +- llarp/apple/vpn_platform.cpp | 32 +- llarp/apple/vpn_platform.hpp | 43 +- llarp/bootstrap.cpp | 259 +- llarp/bootstrap.hpp | 89 +- llarp/bootstrap_fallbacks.cpp | 33 +- llarp/config/config.cpp | 3052 +++++++++---------- llarp/config/config.hpp | 495 ++-- llarp/config/definition.cpp | 505 ++-- llarp/config/definition.hpp | 1069 ++++--- llarp/config/ini.cpp | 441 ++- llarp/config/ini.hpp | 110 +- llarp/config/key_manager.cpp | 334 ++- llarp/config/key_manager.hpp | 126 +- llarp/consensus/reachability_testing.cpp | 254 +- llarp/consensus/reachability_testing.hpp | 246 +- llarp/constants/apple.hpp | 14 +- llarp/constants/files.hpp | 74 +- llarp/constants/link_layer.hpp | 2 +- llarp/constants/net.hpp | 4 +- llarp/constants/path.hpp | 57 +- llarp/constants/platform.hpp | 112 +- llarp/constants/proto.hpp | 6 +- llarp/constants/time.hpp | 2 +- llarp/constants/version.hpp | 12 +- llarp/context.cpp | 324 +-- llarp/crypto/crypto.cpp | 1051 ++++--- llarp/crypto/crypto.hpp | 255 +- llarp/crypto/encrypted.hpp | 289 +- llarp/crypto/types.cpp | 184 +- llarp/crypto/types.hpp | 272 +- llarp/dht/bucket.hpp | 377 ++- llarp/dht/kademlia.hpp | 30 +- llarp/dht/key.hpp | 96 +- llarp/dht/node.hpp | 90 +- llarp/dns/dns.hpp | 34 +- llarp/dns/message.cpp | 749 +++-- llarp/dns/message.hpp | 135 +- llarp/dns/name.cpp | 224 +- llarp/dns/name.hpp | 16 +- llarp/dns/nm_platform.cpp | 9 +- llarp/dns/nm_platform.hpp | 21 +- llarp/dns/platform.cpp | 46 +- llarp/dns/platform.hpp | 85 +- llarp/dns/question.cpp | 212 +- llarp/dns/question.hpp | 75 +- llarp/dns/rr.cpp | 185 +- llarp/dns/rr.hpp | 47 +- llarp/dns/sd_platform.cpp | 207 +- llarp/dns/sd_platform.hpp | 23 +- llarp/dns/serialize.cpp | 58 +- llarp/dns/serialize.hpp | 31 +- llarp/dns/server.cpp | 1306 ++++----- llarp/dns/server.hpp | 490 ++-- llarp/dns/srv_data.cpp | 215 +- llarp/dns/srv_data.hpp | 164 +- llarp/dns/string.hpp | 12 +- llarp/endpoint_base.cpp | 39 +- llarp/endpoint_base.hpp | 219 +- llarp/ev/ev.cpp | 18 +- llarp/ev/ev.hpp | 455 ++- llarp/ev/libuv.cpp | 600 ++-- llarp/ev/libuv.hpp | 100 +- llarp/ev/udp_handle.hpp | 87 +- llarp/exit/context.cpp | 174 +- llarp/exit/context.hpp | 57 +- llarp/exit/endpoint.cpp | 439 ++- llarp/exit/endpoint.hpp | 208 +- llarp/exit/policy.cpp | 50 +- llarp/exit/policy.hpp | 31 +- llarp/exit/session.cpp | 611 ++-- llarp/exit/session.hpp | 265 +- llarp/handlers/common.hpp | 4 +- llarp/handlers/exit.cpp | 1577 +++++----- llarp/handlers/exit.hpp | 283 +- llarp/handlers/null.hpp | 187 +- llarp/handlers/tun.cpp | 2592 ++++++++--------- llarp/handlers/tun.hpp | 477 ++- llarp/link/connection.cpp | 12 +- llarp/link/connection.hpp | 29 +- llarp/link/contacts.cpp | 64 +- llarp/link/contacts.hpp | 62 +- llarp/link/link_manager.cpp | 3401 +++++++++++----------- llarp/link/link_manager.hpp | 792 +++-- llarp/link/tunnel.cpp | 1255 ++++---- llarp/link/tunnel.hpp | 411 ++- llarp/linux/dbus.cpp | 34 +- llarp/linux/dbus.hpp | 96 +- llarp/linux/sd_service_manager.cpp | 89 +- llarp/lokinet_shared.cpp | 1761 ++++++----- llarp/messages/common.hpp | 48 +- llarp/messages/dht.hpp | 176 +- llarp/messages/exit.hpp | 253 +- llarp/messages/fetch.hpp | 196 +- llarp/messages/link_message.hpp | 69 +- llarp/messages/path.hpp | 239 +- llarp/net/bogon_ranges.hpp | 58 +- llarp/net/interface_info.cpp | 19 +- llarp/net/interface_info.hpp | 27 +- llarp/net/ip.cpp | 34 +- llarp/net/ip.hpp | 45 +- llarp/net/ip_address.cpp | 335 +-- llarp/net/ip_address.hpp | 271 +- llarp/net/ip_packet.cpp | 1260 ++++---- llarp/net/ip_packet.hpp | 615 ++-- llarp/net/ip_range.cpp | 204 +- llarp/net/ip_range.hpp | 302 +- llarp/net/ip_range_map.hpp | 265 +- llarp/net/net.h | 9 +- llarp/net/net.hpp | 375 ++- llarp/net/net_bits.hpp | 86 +- llarp/net/net_if.hpp | 5 +- llarp/net/net_int.cpp | 254 +- llarp/net/net_int.hpp | 494 ++-- llarp/net/posix.cpp | 318 +- llarp/net/sock_addr.cpp | 783 +++-- llarp/net/sock_addr.hpp | 309 +- llarp/net/traffic_policy.cpp | 372 ++- llarp/net/traffic_policy.hpp | 94 +- llarp/net/uint128.hpp | 496 ++-- llarp/net/win32.cpp | 321 +- llarp/nodedb.cpp | 1830 ++++++------ llarp/nodedb.hpp | 1037 ++++--- llarp/path/abstracthophandler.cpp | 39 +- llarp/path/abstracthophandler.hpp | 102 +- llarp/path/path.cpp | 868 +++--- llarp/path/path.hpp | 454 ++- llarp/path/path_context.cpp | 379 ++- llarp/path/path_context.hpp | 150 +- llarp/path/path_types.hpp | 82 +- llarp/path/pathbuilder.cpp | 1004 ++++--- llarp/path/pathbuilder.hpp | 172 +- llarp/path/pathset.cpp | 742 +++-- llarp/path/pathset.hpp | 353 +-- llarp/path/transit_hop.cpp | 240 +- llarp/path/transit_hop.hpp | 278 +- llarp/profiling.cpp | 538 ++-- llarp/profiling.hpp | 148 +- llarp/router/route_poker.cpp | 429 ++- llarp/router/route_poker.hpp | 71 +- llarp/router/router.cpp | 2222 +++++++------- llarp/router/router.hpp | 746 +++-- llarp/router_contact.cpp | 317 +- llarp/router_contact.hpp | 642 ++-- llarp/router_contact_local.cpp | 149 +- llarp/router_contact_remote.cpp | 74 +- llarp/router_id.cpp | 69 +- llarp/router_id.hpp | 74 +- llarp/router_version.cpp | 149 +- llarp/router_version.hpp | 82 +- llarp/rpc/endpoint_rpc.cpp | 202 +- llarp/rpc/endpoint_rpc.hpp | 75 +- llarp/rpc/json_binary_proxy.cpp | 92 +- llarp/rpc/json_binary_proxy.hpp | 260 +- llarp/rpc/json_bt.hpp | 63 +- llarp/rpc/json_conversions.cpp | 18 +- llarp/rpc/json_conversions.hpp | 41 +- llarp/rpc/lokid_rpc_client.cpp | 704 ++--- llarp/rpc/lokid_rpc_client.hpp | 150 +- llarp/rpc/param_parser.hpp | 631 ++-- llarp/rpc/rpc_request.hpp | 100 +- llarp/rpc/rpc_request_decorators.hpp | 212 +- llarp/rpc/rpc_request_definitions.hpp | 522 ++-- llarp/rpc/rpc_request_parser.cpp | 170 +- llarp/rpc/rpc_request_parser.hpp | 35 +- llarp/rpc/rpc_server.cpp | 979 +++---- llarp/rpc/rpc_server.hpp | 254 +- llarp/service/address.cpp | 130 +- llarp/service/address.hpp | 157 +- llarp/service/async_key_exchange.cpp | 122 +- llarp/service/async_key_exchange.hpp | 54 +- llarp/service/auth.cpp | 303 +- llarp/service/auth.hpp | 177 +- llarp/service/context.cpp | 347 ++- llarp/service/context.hpp | 102 +- llarp/service/convotag.cpp | 37 +- llarp/service/convotag.hpp | 34 +- llarp/service/endpoint.cpp | 2995 ++++++++++--------- llarp/service/endpoint.hpp | 630 ++-- llarp/service/endpoint_state.cpp | 102 +- llarp/service/endpoint_state.hpp | 67 +- llarp/service/endpoint_types.hpp | 42 +- llarp/service/endpoint_util.cpp | 257 +- llarp/service/endpoint_util.hpp | 114 +- llarp/service/identity.cpp | 308 +- llarp/service/identity.hpp | 76 +- llarp/service/info.cpp | 141 +- llarp/service/info.hpp | 171 +- llarp/service/intro.cpp | 163 +- llarp/service/intro.hpp | 132 +- llarp/service/intro_set.cpp | 870 +++--- llarp/service/intro_set.hpp | 305 +- llarp/service/lns_tracker.cpp | 74 +- llarp/service/lns_tracker.hpp | 70 +- llarp/service/name.cpp | 112 +- llarp/service/name.hpp | 18 +- llarp/service/outbound_context.cpp | 1103 ++++--- llarp/service/outbound_context.hpp | 216 +- llarp/service/pendingbuffer.hpp | 23 +- llarp/service/protocol.cpp | 811 +++--- llarp/service/protocol.hpp | 244 +- llarp/service/protocol_type.hpp | 45 +- llarp/service/router_lookup_job.cpp | 6 +- llarp/service/router_lookup_job.hpp | 40 +- llarp/service/session.cpp | 77 +- llarp/service/session.hpp | 59 +- llarp/service/tag.cpp | 13 +- llarp/service/tag.hpp | 90 +- llarp/service/vanity.hpp | 10 +- llarp/simulation/sim_context.cpp | 44 +- llarp/simulation/sim_context.hpp | 35 +- llarp/util/algorithm.hpp | 32 +- llarp/util/aligned.hpp | 606 ++-- llarp/util/bencode.cpp | 244 +- llarp/util/bencode.h | 27 +- llarp/util/bencode.hpp | 682 +++-- llarp/util/bits.hpp | 96 +- llarp/util/buffer.cpp | 194 +- llarp/util/buffer.hpp | 603 ++-- llarp/util/compare_ptr.hpp | 38 +- llarp/util/copy_or_nullptr.hpp | 9 +- llarp/util/decaying_hashset.hpp | 154 +- llarp/util/decaying_hashtable.hpp | 108 +- llarp/util/exceptions.hpp | 10 +- llarp/util/file.cpp | 164 +- llarp/util/file.hpp | 95 +- llarp/util/formattable.hpp | 78 +- llarp/util/fs.hpp | 8 +- llarp/util/logging.hpp | 201 +- llarp/util/logging/buffer.cpp | 69 +- llarp/util/logging/buffer.hpp | 95 +- llarp/util/logging/callback_sink.hpp | 64 +- llarp/util/lokinet_init.h | 3 +- llarp/util/mem.cpp | 41 +- llarp/util/mem.h | 3 +- llarp/util/mem.hpp | 118 +- llarp/util/nop_service_manager.cpp | 4 +- llarp/util/priority_queue.hpp | 8 +- llarp/util/service_manager.hpp | 197 +- llarp/util/status.hpp | 8 +- llarp/util/str.cpp | 159 +- llarp/util/str.hpp | 151 +- llarp/util/thread/barrier.hpp | 67 +- llarp/util/thread/queue.hpp | 909 +++--- llarp/util/thread/queue_manager.cpp | 864 +++--- llarp/util/thread/queue_manager.hpp | 335 +-- llarp/util/thread/threading.cpp | 43 +- llarp/util/thread/threading.hpp | 223 +- llarp/util/time.cpp | 191 +- llarp/util/time.hpp | 39 +- llarp/util/types.hpp | 15 +- llarp/vpn/android.hpp | 144 +- llarp/vpn/common.hpp | 57 +- llarp/vpn/egres_packet_router.cpp | 139 +- llarp/vpn/egres_packet_router.hpp | 55 +- llarp/vpn/i_packet_io.hpp | 47 +- llarp/vpn/linux.hpp | 759 +++-- llarp/vpn/packet_intercept.hpp | 28 +- llarp/vpn/packet_router.cpp | 116 +- llarp/vpn/packet_router.hpp | 53 +- llarp/vpn/platform.cpp | 30 +- llarp/vpn/platform.hpp | 246 +- llarp/vpn/win32.cpp | 258 +- llarp/vpn/win32.hpp | 82 +- llarp/win32/dll.cpp | 30 +- llarp/win32/dll.hpp | 43 +- llarp/win32/exception.cpp | 56 +- llarp/win32/exception.hpp | 17 +- llarp/win32/exec.cpp | 82 +- llarp/win32/exec.hpp | 73 +- llarp/win32/guid.hpp | 39 +- llarp/win32/handle.hpp | 19 +- llarp/win32/service_manager.cpp | 162 +- llarp/win32/service_manager.hpp | 35 +- llarp/win32/win32_inet.c | 128 +- llarp/win32/win32_intrnl.c | 95 +- llarp/win32/windivert.cpp | 512 ++-- llarp/win32/windivert.hpp | 19 +- llarp/win32/wintun.cpp | 673 +++-- llarp/win32/wintun.hpp | 12 +- pybind/common.hpp | 78 +- pybind/llarp/config.cpp | 220 +- pybind/llarp/context.cpp | 83 +- pybind/llarp/crypto/types.cpp | 31 +- pybind/llarp/dht/dht_types.cpp | 31 +- pybind/llarp/handlers/pyhandler.cpp | 21 +- pybind/llarp/handlers/pyhandler.hpp | 192 +- pybind/llarp/logger.cpp | 43 +- pybind/llarp/path/path_hop_config.cpp | 43 +- pybind/llarp/path/path_types.cpp | 17 +- pybind/llarp/peerstats.cpp | 56 +- pybind/llarp/router.cpp | 28 +- pybind/llarp/router_contact.cpp | 49 +- pybind/llarp/router_id.cpp | 31 +- pybind/llarp/service/address.cpp | 17 +- pybind/llarp/tooling/router_event.cpp | 139 +- pybind/llarp/tooling/router_hive.cpp | 85 +- pybind/module.cpp | 38 +- 329 files changed, 41863 insertions(+), 44791 deletions(-) diff --git a/.clang-format b/.clang-format index 02f6e2e07..56bb4eb6b 100644 --- a/.clang-format +++ b/.clang-format @@ -10,11 +10,20 @@ AllowShortCaseLabelsOnASingleLine: 'false' AllowShortFunctionsOnASingleLine: None AllowShortIfStatementsOnASingleLine: 'false' AllowShortLoopsOnASingleLine: 'false' -AlwaysBreakAfterDefinitionReturnType: All -AlwaysBreakAfterReturnType: All -AlwaysBreakTemplateDeclarations: 'true' -BreakBeforeBinaryOperators: NonAssignment -BreakBeforeBraces: Custom +ColumnLimit: 100 +KeepEmptyLinesAtTheStartOfBlocks: 'false' +NamespaceIndentation: All +PenaltyBreakString: '3' +SortIncludes: CaseInsensitive +SpaceBeforeParens: ControlStatements +SpacesInAngles: 'false' +SpacesInContainerLiterals: 'false' +SpacesInParentheses: 'false' +SpacesInSquareBrackets: 'false' +Standard: Cpp11 +UseTab: Never + +# bracing BraceWrapping: AfterCaseLabel: true AfterClass: true @@ -31,27 +40,27 @@ BraceWrapping: SplitEmptyFunction: false SplitEmptyRecord: false SplitEmptyNamespace: false + +# breaking +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakTemplateDeclarations: 'true' +BreakBeforeBinaryOperators: NonAssignment +BreakBeforeBraces: Custom BreakBeforeTernaryOperators: 'true' -BreakConstructorInitializersBeforeComma: 'true' -ColumnLimit: 100 -Cpp11BracedListStyle: 'true' -KeepEmptyLinesAtTheStartOfBlocks: 'false' -NamespaceIndentation: All -PenaltyBreakString: '3' -SortIncludes: CaseInsensitive -SpaceBeforeParens: ControlStatements -SpacesInAngles: 'false' -SpacesInContainerLiterals: 'false' -SpacesInParentheses: 'false' -SpacesInSquareBrackets: 'false' -Standard: Cpp11 -UseTab: Never +BreakConstructorInitializers: BeforeColon + +# indent width +IndentWidth: 4 +ConstructorInitializerIndentWidth: 4 +ContinuationIndentWidth: 4 # treat pointers and reference declarations as if part of the type DerivePointerAlignment: false PointerAlignment: Left # when wrapping function calls/declarations, force each parameter to have its own line +PackConstructorInitializers: NextLine BinPackParameters: 'false' BinPackArguments: 'false' diff --git a/daemon/lokinet-bootstrap.cpp b/daemon/lokinet-bootstrap.cpp index 22ecb9d6f..76cc3abcc 100644 --- a/daemon/lokinet-bootstrap.cpp +++ b/daemon/lokinet-bootstrap.cpp @@ -14,100 +14,97 @@ namespace { - int - fail(std::string msg) - { - std::cout << msg << std::endl; - return 1; - } - - int - print_help(std::string exe) - { - std::cout << R"(Lokinet bootstrap.signed fetchy program thing + int fail(std::string msg) + { + std::cout << msg << std::endl; + return 1; + } + + int print_help(std::string exe) + { + std::cout << R"(Lokinet bootstrap.signed fetchy program thing Downloads the initial bootstrap.signed for lokinet into a local file from a default or user defined server via the reachable network. -Usage: )" << exe - << R"( [bootstrap_url [output_file]] +Usage: )" << exe << R"( [bootstrap_url [output_file]] bootstrap_url can be specified as a full URL, or a special named value ("mainnet" or "testnet") to download from the pre-defined mainnet or testnet bootstrap URLs. )"; - return 0; - } + return 0; + } } // namespace -int -main(int argc, char* argv[]) +int main(int argc, char* argv[]) { - const std::unordered_map bootstrap_urls = { - {"lokinet", "https://seed.lokinet.org/lokinet.signed"}, - {"testnet", "https://seed.lokinet.org/testnet.signed"}}; + const std::unordered_map bootstrap_urls = { + {"lokinet", "https://seed.lokinet.org/lokinet.signed"}, + {"testnet", "https://seed.lokinet.org/testnet.signed"}}; - std::string bootstrap_url = bootstrap_urls.at("lokinet"); - fs::path outputfile{llarp::GetDefaultBootstrap()}; + std::string bootstrap_url = bootstrap_urls.at("lokinet"); + fs::path outputfile{llarp::GetDefaultBootstrap()}; - const std::unordered_set help_args = {"-h", "--help"}; + const std::unordered_set help_args = {"-h", "--help"}; - for (int idx = 1; idx < argc; idx++) - { - const std::string arg{argv[idx]}; - if (help_args.count(arg)) - return print_help(argv[0]); - } + for (int idx = 1; idx < argc; idx++) + { + const std::string arg{argv[idx]}; + if (help_args.count(arg)) + return print_help(argv[0]); + } - if (argc > 1) - { - if (auto itr = bootstrap_urls.find(argv[1]); itr != bootstrap_urls.end()) + if (argc > 1) { - bootstrap_url = itr->second; + if (auto itr = bootstrap_urls.find(argv[1]); itr != bootstrap_urls.end()) + { + bootstrap_url = itr->second; + } + else + { + bootstrap_url = argv[1]; + } } - else + if (argc > 2) { - bootstrap_url = argv[1]; + outputfile = fs::path{argv[2]}; } - } - if (argc > 2) - { - outputfile = fs::path{argv[2]}; - } - std::cout << "fetching " << bootstrap_url << std::endl; - cpr::Response resp = + std::cout << "fetching " << bootstrap_url << std::endl; + cpr::Response resp = #ifdef _WIN32 - cpr::Get( - cpr::Url{bootstrap_url}, cpr::Header{{"User-Agent", std::string{llarp::VERSION_FULL}}}); + cpr::Get( + cpr::Url{bootstrap_url}, cpr::Header{{"User-Agent", std::string{llarp::VERSION_FULL}}}); #else - cpr::Get( - cpr::Url{bootstrap_url}, - cpr::Header{{"User-Agent", std::string{llarp::LOKINET_VERSION_FULL}}}, - cpr::Ssl(cpr::ssl::CaPath{X509_get_default_cert_dir()})); + cpr::Get( + cpr::Url{bootstrap_url}, + cpr::Header{{"User-Agent", std::string{llarp::LOKINET_VERSION_FULL}}}, + cpr::Ssl(cpr::ssl::CaPath{X509_get_default_cert_dir()})); #endif - if (resp.status_code != 200) - { - return fail("failed to fetch '" + bootstrap_url + "' HTTP " + std::to_string(resp.status_code)); - } - - const auto& data = resp.text; - - if (data[0] == 'l' or data[0] == 'd') - { - try + if (resp.status_code != 200) { - std::cout << "writing bootstrap file to: " << outputfile << std::endl; - fs::ofstream ofs{outputfile, std::ios::binary}; - ofs.exceptions(fs::ofstream::failbit); - ofs << data; - return 0; + return fail( + "failed to fetch '" + bootstrap_url + "' HTTP " + std::to_string(resp.status_code)); } - catch (std::exception& ex) + + const auto& data = resp.text; + + if (data[0] == 'l' or data[0] == 'd') { - return fail(std::string{"failed to write bootstrap file: "} + ex.what()); + try + { + std::cout << "writing bootstrap file to: " << outputfile << std::endl; + fs::ofstream ofs{outputfile, std::ios::binary}; + ofs.exceptions(fs::ofstream::failbit); + ofs << data; + return 0; + } + catch (std::exception& ex) + { + return fail(std::string{"failed to write bootstrap file: "} + ex.what()); + } } - } - return fail("got invalid bootstrap file content"); + return fail("got invalid bootstrap file content"); } diff --git a/daemon/lokinet-vpn.cpp b/daemon/lokinet-vpn.cpp index 2444ab536..16dd35e67 100644 --- a/daemon/lokinet-vpn.cpp +++ b/daemon/lokinet-vpn.cpp @@ -19,276 +19,279 @@ /// do a oxenmq request on an omq instance blocking style /// returns a json object parsed from the result -std::optional -OMQ_Request( +std::optional OMQ_Request( oxenmq::OxenMQ& omq, const oxenmq::ConnectionID& id, std::string_view method, std::optional args = std::nullopt) { - std::promise> result_promise; + std::promise> result_promise; - auto handleRequest = [&result_promise](bool success, std::vector result) { - if ((not success) or result.empty()) + auto handleRequest = [&result_promise](bool success, std::vector result) { + if ((not success) or result.empty()) + { + result_promise.set_value(std::nullopt); + return; + } + result_promise.set_value(result[0]); + }; + if (args.has_value()) + { + omq.request(id, method, handleRequest, args->dump()); + } + else { - result_promise.set_value(std::nullopt); - return; + omq.request(id, method, handleRequest); } - result_promise.set_value(result[0]); - }; - if (args.has_value()) - { - omq.request(id, method, handleRequest, args->dump()); - } - else - { - omq.request(id, method, handleRequest); - } - auto ftr = result_promise.get_future(); - const auto str = ftr.get(); - if (str.has_value()) - return nlohmann::json::parse(*str); - return std::nullopt; + auto ftr = result_promise.get_future(); + const auto str = ftr.get(); + if (str.has_value()) + return nlohmann::json::parse(*str); + return std::nullopt; } namespace { - struct command_line_options - { - // bool options - bool verbose = false; - bool help = false; - bool vpnUp = false; - bool vpnDown = false; - bool swap = false; - bool printStatus = false; - bool killDaemon = false; - - // string options - std::string exitAddress; - std::string rpc; - std::string endpoint = "default"; - std::string token; - std::optional range; - std::vector swapExits; - - // oxenmq - oxenmq::address rpcURL{}; - oxenmq::LogLevel logLevel = oxenmq::LogLevel::warn; - }; - - // Takes a code, prints a message, and returns the code. Intended use is: - // return exit_error(1, "blah: {}", 42); - // from within main(). - template - [[nodiscard]] int - exit_error(int code, const std::string& format, T&&... args) - { - fmt::print(format, std::forward(args)...); - fmt::print("\n"); - return code; - } - - // Same as above, but with code omitted (uses exit code 1) - template - [[nodiscard]] int - exit_error(const std::string& format, T&&... args) - { - return exit_error(1, format, std::forward(args)...); - } + struct command_line_options + { + // bool options + bool verbose = false; + bool help = false; + bool vpnUp = false; + bool vpnDown = false; + bool swap = false; + bool printStatus = false; + bool killDaemon = false; + + // string options + std::string exitAddress; + std::string rpc; + std::string endpoint = "default"; + std::string token; + std::optional range; + std::vector swapExits; + + // oxenmq + oxenmq::address rpcURL{}; + oxenmq::LogLevel logLevel = oxenmq::LogLevel::warn; + }; + + // Takes a code, prints a message, and returns the code. Intended use is: + // return exit_error(1, "blah: {}", 42); + // from within main(). + template + [[nodiscard]] int exit_error(int code, const std::string& format, T&&... args) + { + fmt::print(format, std::forward(args)...); + fmt::print("\n"); + return code; + } + + // Same as above, but with code omitted (uses exit code 1) + template + [[nodiscard]] int exit_error(const std::string& format, T&&... args) + { + return exit_error(1, format, std::forward(args)...); + } } // namespace -int -main(int argc, char* argv[]) +int main(int argc, char* argv[]) { - CLI::App cli{"lokiNET vpn control utility", "lokinet-vpn"}; - command_line_options options{}; - - // flags: boolean values in command_line_options struct - cli.add_flag("-v,--verbose", options.verbose, "Verbose"); - 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,--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(); - cli.add_option( - "--log-level", options.logLevel, "Log verbosity level, see log levels for accepted values") - ->type_name("LEVEL") - ->capture_default_str(); - - try - { - cli.parse(argc, argv); - } - catch (const CLI::ParseError& e) - { - return cli.exit(e); - } - - try - { - if (options.verbose) - options.logLevel = oxenmq::LogLevel::debug; - } - catch (const CLI::OptionNotFound& e) - { - cli.exit(e); - } - catch (const CLI::Error& e) - { - cli.exit(e); - }; - - int numCommands = options.vpnUp + options.vpnDown + options.printStatus + options.killDaemon - + (not options.swapExits.empty()); - - switch (numCommands) - { - case 0: - 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 --add/--remove/--swap/--status/--kill may be specified"); - } - - if (options.vpnUp and options.exitAddress.empty()) - return exit_error("No exit address provided, must specify --exit
"); - - oxenmq::OxenMQ omq{ - [](oxenmq::LogLevel lvl, const char* file, int line, std::string msg) { - std::cout << lvl << " [" << file << ":" << line << "] " << msg << std::endl; - }, - options.logLevel}; - - options.rpcURL = oxenmq::address{(options.rpc.empty()) ? "tcp://127.0.0.1:1190" : options.rpc}; - - omq.start(); - - std::promise connectPromise; - - const auto connectionID = omq.connect_remote( - 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; - connectPromise.set_value(false); - }); - - auto ftr = connectPromise.get_future(); - if (not ftr.get()) - return 1; - - if (options.killDaemon) - { - auto maybe_halt = OMQ_Request(omq, connectionID, "llarp.halt"); - - if (not maybe_halt) - return exit_error("Call to llarp.halt failed"); - - if (auto err_it = maybe_halt->find("error"); - err_it != maybe_halt->end() and not err_it.value().is_null()) + CLI::App cli{"lokiNET vpn control utility", "lokinet-vpn"}; + command_line_options options{}; + + // flags: boolean values in command_line_options struct + cli.add_flag("-v,--verbose", options.verbose, "Verbose"); + 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,--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(); + cli.add_option( + "--log-level", + options.logLevel, + "Log verbosity level, see log levels for accepted values") + ->type_name("LEVEL") + ->capture_default_str(); + + try { - return exit_error("{}", err_it.value().dump()); + cli.parse(argc, argv); + } + catch (const CLI::ParseError& e) + { + return cli.exit(e); } - } - - if (options.printStatus) - { - const auto maybe_status = OMQ_Request(omq, connectionID, "llarp.status"); - - if (not maybe_status) - return exit_error("Call to llarp.status failed"); try { - const auto& ep = maybe_status->at("result").at("services").at(options.endpoint).at("exitMap"); - - if (ep.empty()) - { - std::cout << "No exits found" << std::endl; - } - else - { - for (const auto& [range, exit] : ep.items()) - { - std::cout << range << " via " << exit.get() << std::endl; - } - } + if (options.verbose) + options.logLevel = oxenmq::LogLevel::debug; } - catch (std::exception& ex) + catch (const CLI::OptionNotFound& e) { - return exit_error("Failed to parse result: {}", ex.what()); + cli.exit(e); } - return 0; - } + catch (const CLI::Error& e) + { + cli.exit(e); + }; + + int numCommands = options.vpnUp + options.vpnDown + options.printStatus + options.killDaemon + + (not options.swapExits.empty()); + + switch (numCommands) + { + case 0: + 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 --add/--remove/--swap/--status/--kill may be specified"); + } + + if (options.vpnUp and options.exitAddress.empty()) + return exit_error("No exit address provided, must specify --exit
"); + + oxenmq::OxenMQ omq{ + [](oxenmq::LogLevel lvl, const char* file, int line, std::string msg) { + std::cout << lvl << " [" << file << ":" << line << "] " << msg << std::endl; + }, + options.logLevel}; + + options.rpcURL = oxenmq::address{(options.rpc.empty()) ? "tcp://127.0.0.1:1190" : options.rpc}; - if (not options.swapExits.empty()) - { - nlohmann::json opts{{"exit_addresses", std::move(options.swapExits)}}; + omq.start(); - auto maybe_swap = OMQ_Request(omq, connectionID, "llarp.swap_exits", std::move(opts)); + std::promise connectPromise; - if (not maybe_swap) - return exit_error("Failed to swap exit node connections"); + const auto connectionID = omq.connect_remote( + 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; + connectPromise.set_value(false); + }); - if (auto err_it = maybe_swap->find("error"); - err_it != maybe_swap->end() and not err_it.value().is_null()) + auto ftr = connectPromise.get_future(); + if (not ftr.get()) + return 1; + + if (options.killDaemon) { - return exit_error("{}", err_it.value().dump()); + auto maybe_halt = OMQ_Request(omq, connectionID, "llarp.halt"); + + if (not maybe_halt) + return exit_error("Call to llarp.halt failed"); + + if (auto err_it = maybe_halt->find("error"); + err_it != maybe_halt->end() and not err_it.value().is_null()) + { + return exit_error("{}", err_it.value().dump()); + } } - } - if (options.vpnUp) - { - nlohmann::json opts{{"address", options.exitAddress}, {"token", options.token}}; - if (options.range) - opts["ip_range"] = *options.range; + if (options.printStatus) + { + const auto maybe_status = OMQ_Request(omq, connectionID, "llarp.status"); - auto maybe_result = OMQ_Request(omq, connectionID, "llarp.map_exit", std::move(opts)); + if (not maybe_status) + return exit_error("Call to llarp.status failed"); - if (not maybe_result) - return exit_error("Could not add exit"); + try + { + const auto& ep = + maybe_status->at("result").at("services").at(options.endpoint).at("exitMap"); + + if (ep.empty()) + { + std::cout << "No exits found" << std::endl; + } + else + { + for (const auto& [range, exit] : ep.items()) + { + std::cout << range << " via " << exit.get() << std::endl; + } + } + } + catch (std::exception& ex) + { + return exit_error("Failed to parse result: {}", ex.what()); + } + return 0; + } - if (auto err_it = maybe_result->find("error"); - err_it != maybe_result->end() and not err_it.value().is_null()) + if (not options.swapExits.empty()) { - return exit_error("{}", err_it.value().dump()); + nlohmann::json opts{{"exit_addresses", std::move(options.swapExits)}}; + + auto maybe_swap = OMQ_Request(omq, connectionID, "llarp.swap_exits", std::move(opts)); + + if (not maybe_swap) + return exit_error("Failed to swap exit node connections"); + + if (auto err_it = maybe_swap->find("error"); + err_it != maybe_swap->end() and not err_it.value().is_null()) + { + return exit_error("{}", err_it.value().dump()); + } } - } - if (options.vpnDown) - { - nlohmann::json opts{{"unmap_exit", true}}; - if (options.range) - opts["ip_range"] = *options.range; - auto maybe_down = OMQ_Request(omq, connectionID, "llarp.unmap_exit", std::move(opts)); + if (options.vpnUp) + { + nlohmann::json opts{{"address", options.exitAddress}, {"token", options.token}}; + if (options.range) + opts["ip_range"] = *options.range; + + auto maybe_result = OMQ_Request(omq, connectionID, "llarp.map_exit", std::move(opts)); - if (not maybe_down) - return exit_error("Failed to unmap exit node connection"); + if (not maybe_result) + return exit_error("Could not add exit"); - if (auto err_it = maybe_down->find("error"); - err_it != maybe_down->end() and not err_it.value().is_null()) + if (auto err_it = maybe_result->find("error"); + err_it != maybe_result->end() and not err_it.value().is_null()) + { + return exit_error("{}", err_it.value().dump()); + } + } + if (options.vpnDown) { - return exit_error("{}", err_it.value().dump()); + nlohmann::json opts{{"unmap_exit", true}}; + if (options.range) + opts["ip_range"] = *options.range; + + auto maybe_down = OMQ_Request(omq, connectionID, "llarp.unmap_exit", std::move(opts)); + + if (not maybe_down) + return exit_error("Failed to unmap exit node connection"); + + if (auto err_it = maybe_down->find("error"); + err_it != maybe_down->end() and not err_it.value().is_null()) + { + return exit_error("{}", err_it.value().dump()); + } } - } - return 0; + return 0; } diff --git a/daemon/lokinet.cpp b/daemon/lokinet.cpp index 19c673780..cf4862282 100644 --- a/daemon/lokinet.cpp +++ b/daemon/lokinet.cpp @@ -31,638 +31,626 @@ namespace { - struct command_line_options - { - // bool options - bool help = false; - bool version = false; - bool generate = false; - bool router = false; - bool config = false; - bool configOnly = false; - bool overwrite = false; - - // string options - // TODO: change this to use a std::filesystem::path once we stop using ghc::filesystem on some - // platforms - std::string configPath; - - // windows options - bool win_install = false; - bool win_remove = false; - }; - - // windows-specific function declarations - int - startWinsock(); - void - install_win32_daemon(); - void - uninstall_win32_daemon(); - - // operational function definitions - int - lokinet_main(int, char**); - void - handle_signal(int sig); - static void - run_main_context(std::optional confFile, const llarp::RuntimeOptions opts); - - // variable declarations - static auto logcat = llarp::log::Cat("main"); - std::shared_ptr ctx; - std::promise exit_code; - - // operational function definitions - void - handle_signal(int sig) - { - llarp::log::info(logcat, "Handling signal {}", sig); - if (ctx) - ctx->loop->call([sig] { ctx->HandleSignal(sig); }); - else - std::cerr << "Received signal " << sig << ", but have no context yet. Ignoring!" << std::endl; - } - - // Windows specific code -#ifdef _WIN32 - extern "C" LONG FAR PASCAL - win32_signal_handler(EXCEPTION_POINTERS*); - extern "C" VOID FAR PASCAL - win32_daemon_entry(DWORD, LPTSTR*); - VOID - insert_description(); - - extern "C" BOOL FAR PASCAL - handle_signal_win32(DWORD fdwCtrlType) - { - UNREFERENCED_PARAMETER(fdwCtrlType); - handle_signal(SIGINT); - return TRUE; // probably unreachable - }; - - int - startWinsock() - { - WSADATA wsockd; - int err; - err = ::WSAStartup(MAKEWORD(2, 2), &wsockd); - if (err) + struct command_line_options { - perror("Failed to start Windows Sockets"); - return err; - } - ::CreateMutex(nullptr, FALSE, "lokinet_win32_daemon"); - return 0; - } - - void - install_win32_daemon() - { - SC_HANDLE schSCManager; - SC_HANDLE schService; - std::array szPath{}; - - if (!GetModuleFileName(nullptr, szPath.data(), MAX_PATH)) + // bool options + bool help = false; + bool version = false; + bool generate = false; + bool router = false; + bool config = false; + bool configOnly = false; + bool overwrite = false; + + // string options + // TODO: change this to use a std::filesystem::path once we stop using ghc::filesystem on + // some platforms + std::string configPath; + + // windows options + bool win_install = false; + bool win_remove = false; + }; + + // windows-specific function declarations + int startWinsock(); + void install_win32_daemon(); + void uninstall_win32_daemon(); + + // operational function definitions + int lokinet_main(int, char**); + void handle_signal(int sig); + static void run_main_context( + std::optional confFile, const llarp::RuntimeOptions opts); + + // variable declarations + static auto logcat = llarp::log::Cat("main"); + std::shared_ptr ctx; + std::promise exit_code; + + // operational function definitions + void handle_signal(int sig) { - llarp::log::error(logcat, "Cannot install service {}", GetLastError()); - return; + llarp::log::info(logcat, "Handling signal {}", sig); + if (ctx) + ctx->loop->call([sig] { ctx->HandleSignal(sig); }); + else + std::cerr << "Received signal " << sig << ", but have no context yet. Ignoring!" + << std::endl; } - // Get a handle to the SCM database. - schSCManager = OpenSCManager( - nullptr, // local computer - nullptr, // ServicesActive database - SC_MANAGER_ALL_ACCESS); // full access rights + // Windows specific code +#ifdef _WIN32 + extern "C" LONG FAR PASCAL win32_signal_handler(EXCEPTION_POINTERS*); + extern "C" VOID FAR PASCAL win32_daemon_entry(DWORD, LPTSTR*); + VOID insert_description(); - if (nullptr == schSCManager) + extern "C" BOOL FAR PASCAL handle_signal_win32(DWORD fdwCtrlType) { - llarp::log::error(logcat, "OpenSCManager failed {}", GetLastError()); - return; - } + UNREFERENCED_PARAMETER(fdwCtrlType); + handle_signal(SIGINT); + return TRUE; // probably unreachable + }; - // Create the service - schService = CreateService( - schSCManager, // SCM database - strdup("lokinet"), // name of service - "Lokinet for Windows", // service name to display - SERVICE_ALL_ACCESS, // desired access - SERVICE_WIN32_OWN_PROCESS, // service type - SERVICE_DEMAND_START, // start type - SERVICE_ERROR_NORMAL, // error control type - szPath.data(), // path to service's binary - nullptr, // no load ordering group - nullptr, // no tag identifier - nullptr, // no dependencies - nullptr, // LocalSystem account - nullptr); // no password - - if (schService == nullptr) + int startWinsock() { - llarp::log::error(logcat, "CreateService failed {}", GetLastError()); - CloseServiceHandle(schSCManager); - return; + WSADATA wsockd; + int err; + err = ::WSAStartup(MAKEWORD(2, 2), &wsockd); + if (err) + { + perror("Failed to start Windows Sockets"); + return err; + } + ::CreateMutex(nullptr, FALSE, "lokinet_win32_daemon"); + return 0; } - else - llarp::log::info(logcat, "Service installed successfully"); - - CloseServiceHandle(schService); - CloseServiceHandle(schSCManager); - insert_description(); - } - - VOID - insert_description() - { - SC_HANDLE schSCManager; - SC_HANDLE schService; - SERVICE_DESCRIPTION sd; - LPTSTR szDesc = strdup( - "LokiNET is a free, open source, private, " - "decentralized, \"market based sybil resistant\" " - "and IP based onion routing network"); - // Get a handle to the SCM database. - schSCManager = OpenSCManager( - NULL, // local computer - NULL, // ServicesActive database - SC_MANAGER_ALL_ACCESS); // full access rights - - if (nullptr == schSCManager) + + void install_win32_daemon() { - llarp::log::error(logcat, "OpenSCManager failed {}", GetLastError()); - return; - } + SC_HANDLE schSCManager; + SC_HANDLE schService; + std::array szPath{}; - // Get a handle to the service. - schService = OpenService( - schSCManager, // SCM database - "lokinet", // name of service - SERVICE_CHANGE_CONFIG); // need change config access + if (!GetModuleFileName(nullptr, szPath.data(), MAX_PATH)) + { + llarp::log::error(logcat, "Cannot install service {}", GetLastError()); + return; + } - if (schService == nullptr) - { - llarp::log::error(logcat, "OpenService failed {}", GetLastError()); - CloseServiceHandle(schSCManager); - return; - } + // Get a handle to the SCM database. + schSCManager = OpenSCManager( + nullptr, // local computer + nullptr, // ServicesActive database + SC_MANAGER_ALL_ACCESS); // full access rights - // Change the service description. - sd.lpDescription = szDesc; + if (nullptr == schSCManager) + { + llarp::log::error(logcat, "OpenSCManager failed {}", GetLastError()); + return; + } - if (!ChangeServiceConfig2( - schService, // handle to service - SERVICE_CONFIG_DESCRIPTION, // change: description - &sd)) // new description - { - llarp::log::error(logcat, "ChangeServiceConfig2 failed"); + // Create the service + schService = CreateService( + schSCManager, // SCM database + strdup("lokinet"), // name of service + "Lokinet for Windows", // service name to display + SERVICE_ALL_ACCESS, // desired access + SERVICE_WIN32_OWN_PROCESS, // service type + SERVICE_DEMAND_START, // start type + SERVICE_ERROR_NORMAL, // error control type + szPath.data(), // path to service's binary + nullptr, // no load ordering group + nullptr, // no tag identifier + nullptr, // no dependencies + nullptr, // LocalSystem account + nullptr); // no password + + if (schService == nullptr) + { + llarp::log::error(logcat, "CreateService failed {}", GetLastError()); + CloseServiceHandle(schSCManager); + return; + } + else + llarp::log::info(logcat, "Service installed successfully"); + + CloseServiceHandle(schService); + CloseServiceHandle(schSCManager); + insert_description(); } - else - llarp::log::info(log_cat, "Service description updated successfully."); - CloseServiceHandle(schService); - CloseServiceHandle(schSCManager); - } + VOID insert_description() + { + SC_HANDLE schSCManager; + SC_HANDLE schService; + SERVICE_DESCRIPTION sd; + LPTSTR szDesc = strdup( + "LokiNET is a free, open source, private, " + "decentralized, \"market based sybil resistant\" " + "and IP based onion routing network"); + // Get a handle to the SCM database. + schSCManager = OpenSCManager( + NULL, // local computer + NULL, // ServicesActive database + SC_MANAGER_ALL_ACCESS); // full access rights + + if (nullptr == schSCManager) + { + llarp::log::error(logcat, "OpenSCManager failed {}", GetLastError()); + return; + } - void - uninstall_win32_daemon() - { - SC_HANDLE schSCManager; - SC_HANDLE schService; + // Get a handle to the service. + schService = OpenService( + schSCManager, // SCM database + "lokinet", // name of service + SERVICE_CHANGE_CONFIG); // need change config access - // Get a handle to the SCM database. - schSCManager = OpenSCManager( - nullptr, // local computer - nullptr, // ServicesActive database - SC_MANAGER_ALL_ACCESS); // full access rights + if (schService == nullptr) + { + llarp::log::error(logcat, "OpenService failed {}", GetLastError()); + CloseServiceHandle(schSCManager); + return; + } - if (nullptr == schSCManager) - { - llarp::log::error(logcat, "OpenSCManager failed {}", GetLastError()); - return; - } + // Change the service description. + sd.lpDescription = szDesc; - // Get a handle to the service. - schService = OpenService( - schSCManager, // SCM database - "lokinet", // name of service - 0x10000); // need delete access + if (!ChangeServiceConfig2( + schService, // handle to service + SERVICE_CONFIG_DESCRIPTION, // change: description + &sd)) // new description + { + llarp::log::error(logcat, "ChangeServiceConfig2 failed"); + } + else + llarp::log::info(log_cat, "Service description updated successfully."); - if (schService == nullptr) - { - llarp::log::error(logcat, "OpenService failed {}", GetLastError()); - CloseServiceHandle(schSCManager); - return; + CloseServiceHandle(schService); + CloseServiceHandle(schSCManager); } - // Delete the service. - if (!DeleteService(schService)) + void uninstall_win32_daemon() { - llarp::log::error(logcat, "DeleteService failed {}", GetLastError()); - } - else - llarp::log::info(logcat, "Service deleted successfully"); - - CloseServiceHandle(schService); - CloseServiceHandle(schSCManager); - } - - /// minidump generation for windows jizz - /// will make a coredump when there is an unhandled exception - LONG - GenerateDump(EXCEPTION_POINTERS* pExceptionPointers) - { - const auto flags = - (MINIDUMP_TYPE)(MiniDumpWithFullMemory | MiniDumpWithFullMemoryInfo | MiniDumpWithHandleData | MiniDumpWithUnloadedModules | MiniDumpWithThreadInfo); - - const std::string fname = - fmt::format("C:\\ProgramData\\lokinet\\crash-{}.dump", llarp::time_now_ms().count()); - - HANDLE hDumpFile; - SYSTEMTIME stLocalTime; - GetLocalTime(&stLocalTime); - MINIDUMP_EXCEPTION_INFORMATION ExpParam{}; - - hDumpFile = CreateFile( - fname.c_str(), - GENERIC_READ | GENERIC_WRITE, - FILE_SHARE_WRITE | FILE_SHARE_READ, - 0, - CREATE_ALWAYS, - 0, - 0); - - ExpParam.ExceptionPointers = pExceptionPointers; - ExpParam.ClientPointers = TRUE; - - MiniDumpWriteDump( - GetCurrentProcess(), GetCurrentProcessId(), hDumpFile, flags, &ExpParam, NULL, NULL); - - return 1; - } - - VOID FAR PASCAL - SvcCtrlHandler(DWORD dwCtrl) - { - // Handle the requested control code. - - switch (dwCtrl) - { - case SERVICE_CONTROL_STOP: - // tell service we are stopping - llarp::log::debug(logcat, "Windows service controller gave SERVICE_CONTROL_STOP"); - llarp::sys::service_manager->system_changed_our_state(llarp::sys::ServiceState::Stopping); - handle_signal(SIGINT); - return; + SC_HANDLE schSCManager; + SC_HANDLE schService; + + // Get a handle to the SCM database. + schSCManager = OpenSCManager( + nullptr, // local computer + nullptr, // ServicesActive database + SC_MANAGER_ALL_ACCESS); // full access rights + + if (nullptr == schSCManager) + { + llarp::log::error(logcat, "OpenSCManager failed {}", GetLastError()); + return; + } + + // Get a handle to the service. + schService = OpenService( + schSCManager, // SCM database + "lokinet", // name of service + 0x10000); // need delete access + + if (schService == nullptr) + { + llarp::log::error(logcat, "OpenService failed {}", GetLastError()); + CloseServiceHandle(schSCManager); + return; + } - case SERVICE_CONTROL_INTERROGATE: - // report status - llarp::log::debug(logcat, "Got win32 service interrogate signal"); - llarp::sys::service_manager->report_changed_state(); - return; + // Delete the service. + if (!DeleteService(schService)) + { + llarp::log::error(logcat, "DeleteService failed {}", GetLastError()); + } + else + llarp::log::info(logcat, "Service deleted successfully"); - default: - llarp::log::debug(logcat, "Got win32 unhandled signal {}", dwCtrl); - break; + CloseServiceHandle(schService); + CloseServiceHandle(schSCManager); } - } - - // The win32 daemon entry point is where we go when invoked as a windows service; we do the - // required service dance and then pretend we were invoked via main(). - VOID FAR PASCAL - win32_daemon_entry(DWORD, LPTSTR* argv) - { - // Register the handler function for the service - auto* svc = dynamic_cast(llarp::sys::service_manager); - svc->handle = RegisterServiceCtrlHandler("lokinet", SvcCtrlHandler); - - if (svc->handle == nullptr) + + /// minidump generation for windows jizz + /// will make a coredump when there is an unhandled exception + LONG GenerateDump(EXCEPTION_POINTERS* pExceptionPointers) { - llarp::log::error(logcat, "failed to register daemon control handler"); - return; - } + const auto flags = + (MINIDUMP_TYPE)(MiniDumpWithFullMemory | MiniDumpWithFullMemoryInfo | MiniDumpWithHandleData | MiniDumpWithUnloadedModules | MiniDumpWithThreadInfo); - // we hard code the args to lokinet_main. - // we yoink argv[0] (lokinet.exe path) and pass in the new args. - std::array args = { - reinterpret_cast(argv[0]), - reinterpret_cast(strdup("c:\\programdata\\lokinet\\lokinet.ini")), - reinterpret_cast(0)}; - lokinet_main(args.size() - 1, args.data()); - } + const std::string fname = + fmt::format("C:\\ProgramData\\lokinet\\crash-{}.dump", llarp::time_now_ms().count()); -#endif + HANDLE hDumpFile; + SYSTEMTIME stLocalTime; + GetLocalTime(&stLocalTime); + MINIDUMP_EXCEPTION_INFORMATION ExpParam{}; - int - lokinet_main(int argc, char** argv) - { - llarp::RuntimeOptions opts; - opts.showBanner = false; + hDumpFile = CreateFile( + fname.c_str(), + GENERIC_READ | GENERIC_WRITE, + FILE_SHARE_WRITE | FILE_SHARE_READ, + 0, + CREATE_ALWAYS, + 0, + 0); -#ifdef _WIN32 - if (startWinsock()) - return -1; - SetConsoleCtrlHandler(handle_signal_win32, TRUE); -#endif + ExpParam.ExceptionPointers = pExceptionPointers; + ExpParam.ClientPointers = TRUE; - CLI::App cli{ - "LokiNET is a free, open source, private, decentralized, market-based sybil resistant and " - "IP " - "based onion routing network", - "lokinet"}; - command_line_options options{}; - - // flags: boolean values in command_line_options struct - cli.add_flag("--version", options.version, "Lokinet version"); - cli.add_flag("-g,--generate", options.generate, "Generate default configuration and exit"); - cli.add_flag( - "-r,--router", options.router, "Run lokinet in routing mode instead of client-only mode"); - cli.add_flag("-f,--force", options.overwrite, "Force writing config even if file exists"); - - // options: string - cli.add_option("config,--config", options.configPath, "Path to lokinet.ini configuration file") - ->capture_default_str(); - - if constexpr (llarp::platform::is_windows) - { - cli.add_flag("--install", options.win_install, "Install win32 daemon to SCM"); - cli.add_flag("--remove", options.win_remove, "Remove win32 daemon from SCM"); + MiniDumpWriteDump( + GetCurrentProcess(), GetCurrentProcessId(), hDumpFile, flags, &ExpParam, NULL, NULL); + + return 1; } - try + VOID FAR PASCAL SvcCtrlHandler(DWORD dwCtrl) { - cli.parse(argc, argv); + // Handle the requested control code. + + switch (dwCtrl) + { + case SERVICE_CONTROL_STOP: + // tell service we are stopping + llarp::log::debug(logcat, "Windows service controller gave SERVICE_CONTROL_STOP"); + llarp::sys::service_manager->system_changed_our_state( + llarp::sys::ServiceState::Stopping); + handle_signal(SIGINT); + return; + + case SERVICE_CONTROL_INTERROGATE: + // report status + llarp::log::debug(logcat, "Got win32 service interrogate signal"); + llarp::sys::service_manager->report_changed_state(); + return; + + default: + llarp::log::debug(logcat, "Got win32 unhandled signal {}", dwCtrl); + break; + } } - catch (const CLI::ParseError& e) + + // The win32 daemon entry point is where we go when invoked as a windows service; we do the + // required service dance and then pretend we were invoked via main(). + VOID FAR PASCAL win32_daemon_entry(DWORD, LPTSTR* argv) { - return cli.exit(e); + // Register the handler function for the service + auto* svc = dynamic_cast(llarp::sys::service_manager); + svc->handle = RegisterServiceCtrlHandler("lokinet", SvcCtrlHandler); + + if (svc->handle == nullptr) + { + llarp::log::error(logcat, "failed to register daemon control handler"); + return; + } + + // we hard code the args to lokinet_main. + // we yoink argv[0] (lokinet.exe path) and pass in the new args. + std::array args = { + reinterpret_cast(argv[0]), + reinterpret_cast(strdup("c:\\programdata\\lokinet\\lokinet.ini")), + reinterpret_cast(0)}; + lokinet_main(args.size() - 1, args.data()); } - std::optional configFile; +#endif - try + int lokinet_main(int argc, char** argv) { - if (options.version) - { - std::cout << llarp::LOKINET_VERSION_FULL << std::endl; - return 0; - } + llarp::RuntimeOptions opts; + opts.showBanner = false; - if constexpr (llarp::platform::is_windows) - { - if (options.win_install) +#ifdef _WIN32 + if (startWinsock()) + return -1; + SetConsoleCtrlHandler(handle_signal_win32, TRUE); +#endif + + CLI::App cli{ + "LokiNET is a free, open source, private, decentralized, market-based sybil resistant " + "and " + "IP " + "based onion routing network", + "lokinet"}; + command_line_options options{}; + + // flags: boolean values in command_line_options struct + cli.add_flag("--version", options.version, "Lokinet version"); + cli.add_flag("-g,--generate", options.generate, "Generate default configuration and exit"); + cli.add_flag( + "-r,--router", + options.router, + "Run lokinet in routing mode instead of client-only mode"); + cli.add_flag("-f,--force", options.overwrite, "Force writing config even if file exists"); + + // options: string + cli.add_option( + "config,--config", options.configPath, "Path to lokinet.ini configuration file") + ->capture_default_str(); + + if constexpr (llarp::platform::is_windows) { - install_win32_daemon(); - return 0; + cli.add_flag("--install", options.win_install, "Install win32 daemon to SCM"); + cli.add_flag("--remove", options.win_remove, "Remove win32 daemon from SCM"); } - if (options.win_remove) + + try { - uninstall_win32_daemon(); - return 0; + cli.parse(argc, argv); + } + catch (const CLI::ParseError& e) + { + return cli.exit(e); } - } - - opts.isSNode = options.router; - if (options.generate) - { - options.configOnly = true; - } + std::optional configFile; - if (not options.configPath.empty()) - { - configFile = options.configPath; - } - } - catch (const CLI::OptionNotFound& e) - { - cli.exit(e); - } - catch (const CLI::Error& e) - { - cli.exit(e); - }; - - if (configFile.has_value()) - { - // when we have an explicit filepath - fs::path basedir = configFile->parent_path(); - if (options.configOnly) - { try { - llarp::ensure_config(basedir, *configFile, options.overwrite, opts.isSNode); + if (options.version) + { + std::cout << llarp::LOKINET_VERSION_FULL << std::endl; + return 0; + } + + if constexpr (llarp::platform::is_windows) + { + if (options.win_install) + { + install_win32_daemon(); + return 0; + } + if (options.win_remove) + { + uninstall_win32_daemon(); + return 0; + } + } + + opts.isSNode = options.router; + + if (options.generate) + { + options.configOnly = true; + } + + if (not options.configPath.empty()) + { + configFile = options.configPath; + } } - catch (std::exception& ex) + catch (const CLI::OptionNotFound& e) { - llarp::log::error(logcat, "cannot generate config at {}: {}", *configFile, ex.what()); - return 1; + cli.exit(e); } - } - else - { - try + catch (const CLI::Error& e) + { + cli.exit(e); + }; + + if (configFile.has_value()) { - if (!fs::exists(*configFile)) - { - llarp::log::error(logcat, "Config file not found {}", *configFile); - return 1; - } + // when we have an explicit filepath + fs::path basedir = configFile->parent_path(); + if (options.configOnly) + { + try + { + llarp::ensure_config(basedir, *configFile, options.overwrite, opts.isSNode); + } + catch (std::exception& ex) + { + llarp::log::error( + logcat, "cannot generate config at {}: {}", *configFile, ex.what()); + return 1; + } + } + else + { + try + { + if (!fs::exists(*configFile)) + { + llarp::log::error(logcat, "Config file not found {}", *configFile); + return 1; + } + } + catch (std::exception& ex) + { + llarp::log::error( + logcat, "cannot check if ", *configFile, " exists: ", ex.what()); + return 1; + } + } } - catch (std::exception& ex) + else { - llarp::log::error(logcat, "cannot check if ", *configFile, " exists: ", ex.what()); - return 1; + try + { + llarp::ensure_config( + llarp::GetDefaultDataDir(), + llarp::GetDefaultConfigPath(), + options.overwrite, + opts.isSNode); + } + catch (std::exception& ex) + { + llarp::log::error(logcat, "cannot ensure config: {}", ex.what()); + return 1; + } + configFile = llarp::GetDefaultConfigPath(); } - } - } - else - { - try - { - llarp::ensure_config( - llarp::GetDefaultDataDir(), - llarp::GetDefaultConfigPath(), - options.overwrite, - opts.isSNode); - } - catch (std::exception& ex) - { - llarp::log::error(logcat, "cannot ensure config: {}", ex.what()); - return 1; - } - configFile = llarp::GetDefaultConfigPath(); - } - if (options.configOnly) - return 0; + if (options.configOnly) + return 0; #ifdef _WIN32 - SetUnhandledExceptionFilter(&GenerateDump); + SetUnhandledExceptionFilter(&GenerateDump); #endif - std::thread main_thread{[configFile, opts] { run_main_context(configFile, opts); }}; - auto ftr = exit_code.get_future(); + std::thread main_thread{[configFile, opts] { run_main_context(configFile, opts); }}; + auto ftr = exit_code.get_future(); - do - { - // do periodic non lokinet related tasks here - if (ctx and ctx->IsUp() and not ctx->LooksAlive()) - { - auto deadlock_cat = llarp::log::Cat("deadlock"); - llarp::log::critical(deadlock_cat, "Router is deadlocked!"); - llarp::log::flush(); - llarp::sys::service_manager->failed(); - std::abort(); - } - } while (ftr.wait_for(std::chrono::seconds(1)) != std::future_status::ready); + do + { + // do periodic non lokinet related tasks here + if (ctx and ctx->IsUp() and not ctx->LooksAlive()) + { + auto deadlock_cat = llarp::log::Cat("deadlock"); + llarp::log::critical(deadlock_cat, "Router is deadlocked!"); + llarp::log::flush(); + llarp::sys::service_manager->failed(); + std::abort(); + } + } while (ftr.wait_for(std::chrono::seconds(1)) != std::future_status::ready); - main_thread.join(); + main_thread.join(); - int code = 0; + int code = 0; - try - { - code = ftr.get(); - } - catch (const std::exception& e) - { - std::cerr << "main thread threw exception: " << e.what() << std::endl; - code = 1; - } - catch (...) - { - std::cerr << "main thread threw non-standard exception" << std::endl; - code = 2; - } + try + { + code = ftr.get(); + } + catch (const std::exception& e) + { + std::cerr << "main thread threw exception: " << e.what() << std::endl; + code = 1; + } + catch (...) + { + std::cerr << "main thread threw non-standard exception" << std::endl; + code = 2; + } - llarp::log::flush(); - llarp::sys::service_manager->stopped(); - if (ctx) - { - ctx.reset(); + llarp::log::flush(); + llarp::sys::service_manager->stopped(); + if (ctx) + { + ctx.reset(); + } + return code; } - return code; - } - - // this sets up, configures and runs the main context - static void - run_main_context(std::optional confFile, const llarp::RuntimeOptions opts) - { - llarp::log::info(logcat, "starting up {}", llarp::LOKINET_VERSION_FULL); - try + + // this sets up, configures and runs the main context + static void run_main_context(std::optional confFile, const llarp::RuntimeOptions opts) { - std::shared_ptr conf; - if (confFile) - { - llarp::log::info(logcat, "Using config file: {}", *confFile); - conf = std::make_shared(confFile->parent_path()); - } - else - { - conf = std::make_shared(llarp::GetDefaultDataDir()); - } - if (not conf->load(confFile, opts.isSNode)) - { - llarp::log::error(logcat, "failed to parse configuration"); - exit_code.set_value(1); - return; - } - - // change cwd to dataDir to support relative paths in config - fs::current_path(conf->router.data_dir); - - ctx = std::make_shared(); - ctx->Configure(std::move(conf)); - - signal(SIGINT, handle_signal); - signal(SIGTERM, handle_signal); + llarp::log::info(logcat, "starting up {}", llarp::LOKINET_VERSION_FULL); + try + { + std::shared_ptr conf; + if (confFile) + { + llarp::log::info(logcat, "Using config file: {}", *confFile); + conf = std::make_shared(confFile->parent_path()); + } + else + { + conf = std::make_shared(llarp::GetDefaultDataDir()); + } + if (not conf->load(confFile, opts.isSNode)) + { + llarp::log::error(logcat, "failed to parse configuration"); + exit_code.set_value(1); + return; + } + + // change cwd to dataDir to support relative paths in config + fs::current_path(conf->router.data_dir); + + ctx = std::make_shared(); + ctx->Configure(std::move(conf)); + + signal(SIGINT, handle_signal); + signal(SIGTERM, handle_signal); #ifndef _WIN32 - signal(SIGHUP, handle_signal); - signal(SIGUSR1, handle_signal); + signal(SIGHUP, handle_signal); + signal(SIGUSR1, handle_signal); #endif - try - { - ctx->Setup(opts); - } - catch (llarp::util::bind_socket_error& ex) - { - llarp::log::error(logcat, "{}; is lokinet already running?", ex.what()); - exit_code.set_value(1); - return; - } - catch (const std::exception& ex) - { - llarp::log::error(logcat, "failed to start up lokinet: {}", ex.what()); - exit_code.set_value(1); - return; - } - llarp::util::SetThreadName("llarp-mainloop"); - - auto result = ctx->Run(opts); - exit_code.set_value(result); - } - catch (const std::exception& e) - { - llarp::log::error(logcat, "Fatal: caught exception while running: {}", e.what()); - exit_code.set_exception(std::current_exception()); - } - catch (...) - { - llarp::log::error(logcat, "Fatal: caught non-standard exception while running"); - exit_code.set_exception(std::current_exception()); + try + { + ctx->Setup(opts); + } + catch (llarp::util::bind_socket_error& ex) + { + llarp::log::error(logcat, "{}; is lokinet already running?", ex.what()); + exit_code.set_value(1); + return; + } + catch (const std::exception& ex) + { + llarp::log::error(logcat, "failed to start up lokinet: {}", ex.what()); + exit_code.set_value(1); + return; + } + llarp::util::SetThreadName("llarp-mainloop"); + + auto result = ctx->Run(opts); + exit_code.set_value(result); + } + catch (const std::exception& e) + { + llarp::log::error(logcat, "Fatal: caught exception while running: {}", e.what()); + exit_code.set_exception(std::current_exception()); + } + catch (...) + { + llarp::log::error(logcat, "Fatal: caught non-standard exception while running"); + exit_code.set_exception(std::current_exception()); + } } - } } // namespace -int -main(int argc, char* argv[]) +int main(int argc, char* argv[]) { - // Set up a default, stderr logging for very early logging; we'll replace this later once we read - // the desired log info from config. - llarp::log::add_sink(llarp::log::Type::Print, "stderr"); - llarp::log::reset_level(llarp::log::Level::info); + // Set up a default, stderr logging for very early logging; we'll replace this later once we + // read the desired log info from config. + llarp::log::add_sink(llarp::log::Type::Print, "stderr"); + llarp::log::reset_level(llarp::log::Level::info); - llarp::logRingBuffer = std::make_shared(100); - llarp::log::add_sink(llarp::logRingBuffer, llarp::log::DEFAULT_PATTERN_MONO); + llarp::logRingBuffer = std::make_shared(100); + llarp::log::add_sink(llarp::logRingBuffer, llarp::log::DEFAULT_PATTERN_MONO); #ifndef _WIN32 - return lokinet_main(argc, argv); + return lokinet_main(argc, argv); #else - if (auto hntdll = GetModuleHandle("ntdll.dll")) - { - if (GetProcAddress(hntdll, "wine_get_version")) + if (auto hntdll = GetModuleHandle("ntdll.dll")) { - static const char* text = "Don't run lokinet in wine, aborting startup"; - static const char* title = "Lokinet Wine Error"; - MessageBoxA(NULL, text, title, MB_ICONHAND); - std::abort(); + if (GetProcAddress(hntdll, "wine_get_version")) + { + static const char* text = "Don't run lokinet in wine, aborting startup"; + static const char* title = "Lokinet Wine Error"; + MessageBoxA(NULL, text, title, MB_ICONHAND); + std::abort(); + } } - } - SERVICE_TABLE_ENTRY DispatchTable[] = { - {strdup("lokinet"), (LPSERVICE_MAIN_FUNCTION)win32_daemon_entry}, {NULL, NULL}}; + SERVICE_TABLE_ENTRY DispatchTable[] = { + {strdup("lokinet"), (LPSERVICE_MAIN_FUNCTION)win32_daemon_entry}, {NULL, NULL}}; - // Try first to run as a service; if this works it fires off to win32_daemon_entry and doesn't - // return until the service enters STOPPED state. - if (StartServiceCtrlDispatcher(DispatchTable)) - return 0; + // Try first to run as a service; if this works it fires off to win32_daemon_entry and doesn't + // return until the service enters STOPPED state. + if (StartServiceCtrlDispatcher(DispatchTable)) + return 0; - auto error = GetLastError(); + auto error = GetLastError(); - // We'll get this error if not invoked as a service, which is fine: we can just run directly - if (error == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) - { - llarp::sys::service_manager->disable(); - return lokinet_main(argc, argv); - } - else - { - llarp::log::critical( - logcat, "Error launching service: {}", std::system_category().message(error)); - return 1; - } + // We'll get this error if not invoked as a service, which is fine: we can just run directly + if (error == ERROR_FAILED_SERVICE_CONTROLLER_CONNECT) + { + llarp::sys::service_manager->disable(); + return lokinet_main(argc, argv); + } + else + { + llarp::log::critical( + logcat, "Error launching service: {}", std::system_category().message(error)); + return 1; + } #endif } diff --git a/external/oxen-libquic b/external/oxen-libquic index ec2e92cd4..28ae47f5b 160000 --- a/external/oxen-libquic +++ b/external/oxen-libquic @@ -1 +1 @@ -Subproject commit ec2e92cd4ace40708f595fb2c608a063f63f3cd2 +Subproject commit 28ae47f5bf5f59cd71a82979bbc96660ca70e8c4 diff --git a/include/llarp.hpp b/include/llarp.hpp index 0767ff33e..c13335118 100644 --- a/include/llarp.hpp +++ b/include/llarp.hpp @@ -10,106 +10,90 @@ namespace llarp { - namespace vpn - { - class Platform; - } - - class EventLoop; - struct Config; - struct RouterContact; - struct Config; - struct Router; - class NodeDB; - - namespace thread - { - class ThreadPool; - } - - struct RuntimeOptions - { - bool showBanner = true; - bool debug = false; - bool isSNode = false; - }; - - struct Context - { - std::shared_ptr router = nullptr; - std::shared_ptr loop = nullptr; - std::shared_ptr nodedb = nullptr; - - Context(); - virtual ~Context() = default; - - void - Setup(const RuntimeOptions& opts); - - int - Run(const RuntimeOptions& opts); - - void - HandleSignal(int sig); - - /// Configure given the specified config. - void - Configure(std::shared_ptr conf); - - /// handle SIGHUP - void - Reload(); - - bool - IsUp() const; - - bool - LooksAlive() const; - - bool - IsStopping() const; - - /// close async - void - CloseAsync(); - - /// wait until closed and done - void - Wait(); - - /// call a function in logic thread - /// return true if queued for calling - /// return false if not queued for calling - bool - CallSafe(std::function f); - - /// Creates a router. Can be overridden to allow a different class of router - /// to be created instead. Defaults to llarp::Router. - virtual std::shared_ptr - makeRouter(const std::shared_ptr& loop); - - /// create the nodedb given our current configs - virtual std::shared_ptr - makeNodeDB(); - - /// create the vpn platform for use in creating network interfaces - virtual std::shared_ptr - makeVPNPlatform(); - - int androidFD = -1; - - protected: - std::shared_ptr config = nullptr; - - private: - void - SigINT(); - - void - Close(); - - std::unique_ptr> closeWaiter; - }; + namespace vpn + { + class Platform; + } + + class EventLoop; + struct Config; + struct RouterContact; + struct Config; + struct Router; + class NodeDB; + + namespace thread + { + class ThreadPool; + } + + struct RuntimeOptions + { + bool showBanner = true; + bool debug = false; + bool isSNode = false; + }; + + struct Context + { + std::shared_ptr router = nullptr; + std::shared_ptr loop = nullptr; + std::shared_ptr nodedb = nullptr; + + Context(); + virtual ~Context() = default; + + void Setup(const RuntimeOptions& opts); + + int Run(const RuntimeOptions& opts); + + void HandleSignal(int sig); + + /// Configure given the specified config. + void Configure(std::shared_ptr conf); + + /// handle SIGHUP + void Reload(); + + bool IsUp() const; + + bool LooksAlive() const; + + bool IsStopping() const; + + /// close async + void CloseAsync(); + + /// wait until closed and done + void Wait(); + + /// call a function in logic thread + /// return true if queued for calling + /// return false if not queued for calling + bool CallSafe(std::function f); + + /// Creates a router. Can be overridden to allow a different class of router + /// to be created instead. Defaults to llarp::Router. + virtual std::shared_ptr makeRouter(const std::shared_ptr& loop); + + /// create the nodedb given our current configs + virtual std::shared_ptr makeNodeDB(); + + /// create the vpn platform for use in creating network interfaces + virtual std::shared_ptr makeVPNPlatform(); + + int androidFD = -1; + + protected: + std::shared_ptr config = nullptr; + + private: + void SigINT(); + + void Close(); + + std::unique_ptr> closeWaiter; + }; } // namespace llarp #endif diff --git a/include/lokinet/lokinet_addr.h b/include/lokinet/lokinet_addr.h index cd36fc2e6..03014de0e 100644 --- a/include/lokinet/lokinet_addr.h +++ b/include/lokinet/lokinet_addr.h @@ -6,10 +6,9 @@ extern "C" { #endif - /// get a free()-able null terminated string that holds our .loki address - /// returns NULL if we dont have one right now - char* EXPORT - lokinet_address(struct lokinet_context*); + /// get a free()-able null terminated string that holds our .loki address + /// returns NULL if we dont have one right now + char* EXPORT lokinet_address(struct lokinet_context*); #ifdef __cplusplus } #endif diff --git a/include/lokinet/lokinet_context.h b/include/lokinet/lokinet_context.h index f3281e28c..53065ff1e 100644 --- a/include/lokinet/lokinet_context.h +++ b/include/lokinet/lokinet_context.h @@ -11,44 +11,37 @@ extern "C" { #endif - struct lokinet_context; - - /// allocate a new lokinet context - struct lokinet_context* EXPORT - lokinet_context_new(); - - /// free a context allocated by lokinet_context_new - void EXPORT - lokinet_context_free(struct lokinet_context*); - - /// spawn all the threads needed for operation and start running - /// return 0 on success - /// return non zero on fail - int EXPORT - lokinet_context_start(struct lokinet_context*); - - /// return 0 if we our endpoint has published on the network and is ready to send - /// return -1 if we don't have enough paths ready - /// retrun -2 if we look deadlocked - /// retrun -3 if context was null or not started yet - int EXPORT - lokinet_status(struct lokinet_context*); - - /// wait at most N milliseconds for lokinet to build paths and get ready - /// return 0 if we are ready - /// return nonzero if we are not ready - int EXPORT - lokinet_wait_for_ready(int N, struct lokinet_context*); - - /// stop all operations on this lokinet context - void EXPORT - lokinet_context_stop(struct lokinet_context*); - - /// load a bootstrap RC from memory - /// return 0 on success - /// return non zero on fail - int EXPORT - lokinet_add_bootstrap_rc(const char*, size_t, struct lokinet_context*); + struct lokinet_context; + + /// allocate a new lokinet context + struct lokinet_context* EXPORT lokinet_context_new(); + + /// free a context allocated by lokinet_context_new + void EXPORT lokinet_context_free(struct lokinet_context*); + + /// spawn all the threads needed for operation and start running + /// return 0 on success + /// return non zero on fail + int EXPORT lokinet_context_start(struct lokinet_context*); + + /// return 0 if we our endpoint has published on the network and is ready to send + /// return -1 if we don't have enough paths ready + /// retrun -2 if we look deadlocked + /// retrun -3 if context was null or not started yet + int EXPORT lokinet_status(struct lokinet_context*); + + /// wait at most N milliseconds for lokinet to build paths and get ready + /// return 0 if we are ready + /// return nonzero if we are not ready + int EXPORT lokinet_wait_for_ready(int N, struct lokinet_context*); + + /// stop all operations on this lokinet context + void EXPORT lokinet_context_stop(struct lokinet_context*); + + /// load a bootstrap RC from memory + /// return 0 on success + /// return non zero on fail + int EXPORT lokinet_add_bootstrap_rc(const char*, size_t, struct lokinet_context*); #ifdef __cplusplus } diff --git a/include/lokinet/lokinet_misc.h b/include/lokinet/lokinet_misc.h index cc928a0a8..01c35d801 100644 --- a/include/lokinet/lokinet_misc.h +++ b/include/lokinet/lokinet_misc.h @@ -5,42 +5,37 @@ extern "C" { #endif - /// change our network id globally across all contexts - void EXPORT - lokinet_set_netid(const char* netid); - - /// get our current netid - /// must be free()'d after use - const char* EXPORT - lokinet_get_netid(); - - /// set log level - /// possible values: trace, debug, info, warn, error, critical, none - /// return 0 on success - /// return non zero on fail - int EXPORT - lokinet_log_level(const char* level); - - /// Function pointer to invoke with lokinet log messages - typedef void (*lokinet_logger_func)(const char* message, void* context); - - /// Optional function to call when flushing lokinet log messages; can be NULL if flushing is not - /// meaningful for the logging system. - typedef void (*lokinet_logger_sync)(void* context); - - /// set a custom logger function; it is safe (and often desirable) to call this before calling - /// initializing lokinet via lokinet_context_new. - void EXPORT - lokinet_set_syncing_logger(lokinet_logger_func func, lokinet_logger_sync sync, void* context); - - /// shortcut for calling `lokinet_set_syncing_logger` with a NULL sync - void EXPORT - lokinet_set_logger(lokinet_logger_func func, void* context); - - /// @brief take in hex and turn it into base32z - /// @return value must be free()'d later - char* EXPORT - lokinet_hex_to_base32z(const char* hex); + /// change our network id globally across all contexts + void EXPORT lokinet_set_netid(const char* netid); + + /// get our current netid + /// must be free()'d after use + const char* EXPORT lokinet_get_netid(); + + /// set log level + /// possible values: trace, debug, info, warn, error, critical, none + /// return 0 on success + /// return non zero on fail + int EXPORT lokinet_log_level(const char* level); + + /// Function pointer to invoke with lokinet log messages + typedef void (*lokinet_logger_func)(const char* message, void* context); + + /// Optional function to call when flushing lokinet log messages; can be NULL if flushing is not + /// meaningful for the logging system. + typedef void (*lokinet_logger_sync)(void* context); + + /// set a custom logger function; it is safe (and often desirable) to call this before calling + /// initializing lokinet via lokinet_context_new. + void EXPORT + lokinet_set_syncing_logger(lokinet_logger_func func, lokinet_logger_sync sync, void* context); + + /// shortcut for calling `lokinet_set_syncing_logger` with a NULL sync + void EXPORT lokinet_set_logger(lokinet_logger_func func, void* context); + + /// @brief take in hex and turn it into base32z + /// @return value must be free()'d later + char* EXPORT lokinet_hex_to_base32z(const char* hex); #ifdef __cplusplus } diff --git a/include/lokinet/lokinet_socket.h b/include/lokinet/lokinet_socket.h index f8aff201d..3a0b7adc7 100644 --- a/include/lokinet/lokinet_socket.h +++ b/include/lokinet/lokinet_socket.h @@ -8,15 +8,13 @@ extern "C" { #endif - /// poll many sockets for activity - /// each pollfd.fd should be set to the socket id - /// returns 0 on sucess - int EXPORT - lokinet_poll(struct pollfd* poll, nfds_t numsockets, struct lokinet_context* ctx); + /// poll many sockets for activity + /// each pollfd.fd should be set to the socket id + /// returns 0 on sucess + int EXPORT lokinet_poll(struct pollfd* poll, nfds_t numsockets, struct lokinet_context* ctx); - /// close a udp socket or a stream socket by its id - void EXPORT - lokinet_close_socket(int id, struct lokinet_context* ctx); + /// close a udp socket or a stream socket by its id + void EXPORT lokinet_close_socket(int id, struct lokinet_context* ctx); #ifdef __cplusplus } diff --git a/include/lokinet/lokinet_srv.h b/include/lokinet/lokinet_srv.h index 27110ab78..b9e8ce369 100644 --- a/include/lokinet/lokinet_srv.h +++ b/include/lokinet/lokinet_srv.h @@ -7,57 +7,54 @@ extern "C" { #endif - // a single srv record - struct lokinet_srv_record - { - /// the srv priority of the record - uint16_t priority; - /// the weight of this record - uint16_t weight; - /// null terminated string of the hostname - char target[256]; - /// the port to use - int port; - }; - - /// private members of a srv lookup - struct lokinet_srv_lookup_private; - - /// the result of an srv lookup - struct lokinet_srv_lookup_result - { - /// set to zero on success otherwise is the error code - int error; - /// pointer to internal members - /// dont touch me - struct lokinet_srv_lookup_private* internal; - }; - - /// do a srv lookup on host for service - /// caller MUST call lokinet_srv_lookup_done when they are done handling the result - int EXPORT - lokinet_srv_lookup( - char* host, - char* service, - struct lokinet_srv_lookup_result* result, - struct lokinet_context* ctx); - - /// a hook function to handle each srv record in a srv lookup result - /// passes in NULL when we are at the end of iteration - /// passes in void * user data - /// hook should NOT free the record - typedef void (*lokinet_srv_record_iterator)(struct lokinet_srv_record*, void*); - - /// iterate over each srv record in a lookup result - /// user is passes into hook and called for each result and then with NULL as the result on the - /// end of iteration - void EXPORT - lokinet_for_each_srv_record( - struct lokinet_srv_lookup_result* result, lokinet_srv_record_iterator iter, void* user); - - /// free internal members of a srv lookup result after use of the result - void EXPORT - lokinet_srv_lookup_done(struct lokinet_srv_lookup_result* result); + // a single srv record + struct lokinet_srv_record + { + /// the srv priority of the record + uint16_t priority; + /// the weight of this record + uint16_t weight; + /// null terminated string of the hostname + char target[256]; + /// the port to use + int port; + }; + + /// private members of a srv lookup + struct lokinet_srv_lookup_private; + + /// the result of an srv lookup + struct lokinet_srv_lookup_result + { + /// set to zero on success otherwise is the error code + int error; + /// pointer to internal members + /// dont touch me + struct lokinet_srv_lookup_private* internal; + }; + + /// do a srv lookup on host for service + /// caller MUST call lokinet_srv_lookup_done when they are done handling the result + int EXPORT lokinet_srv_lookup( + char* host, + char* service, + struct lokinet_srv_lookup_result* result, + struct lokinet_context* ctx); + + /// a hook function to handle each srv record in a srv lookup result + /// passes in NULL when we are at the end of iteration + /// passes in void * user data + /// hook should NOT free the record + typedef void (*lokinet_srv_record_iterator)(struct lokinet_srv_record*, void*); + + /// iterate over each srv record in a lookup result + /// user is passes into hook and called for each result and then with NULL as the result on the + /// end of iteration + void EXPORT lokinet_for_each_srv_record( + struct lokinet_srv_lookup_result* result, lokinet_srv_record_iterator iter, void* user); + + /// free internal members of a srv lookup result after use of the result + void EXPORT lokinet_srv_lookup_done(struct lokinet_srv_lookup_result* result); #ifdef __cplusplus } diff --git a/include/lokinet/lokinet_stream.h b/include/lokinet/lokinet_stream.h index 385a67fc8..9f0c02975 100644 --- a/include/lokinet/lokinet_stream.h +++ b/include/lokinet/lokinet_stream.h @@ -7,52 +7,48 @@ extern "C" { #endif - /// the result of a lokinet stream mapping attempt - struct lokinet_stream_result - { - /// set to zero on success otherwise the error that happened - /// use strerror(3) to get printable string of this error - int error; - - /// the local ip address we mapped the remote endpoint to - /// null terminated - char local_address[256]; - /// the local port we mapped the remote endpoint to - int local_port; - /// the id of the stream we created - int stream_id; - }; - - /// connect out to a remote endpoint - /// remoteAddr is in the form of "name:port" - /// localAddr is either NULL for any or in the form of "ip:port" to bind to an explicit address - void EXPORT - lokinet_outbound_stream( - struct lokinet_stream_result* result, - const char* remoteAddr, - const char* localAddr, - struct lokinet_context* context); - - /// stream accept filter determines if we should accept a stream or not - /// return 0 to accept - /// return -1 to explicitly reject - /// return -2 to silently drop - typedef int (*lokinet_stream_filter)(const char* remote, uint16_t port, void* userdata); - - /// set stream accepter filter - /// passes user parameter into stream filter as void * - /// returns stream id - int EXPORT - lokinet_inbound_stream_filter( - lokinet_stream_filter acceptFilter, void* user, struct lokinet_context* context); - - /// simple stream acceptor - /// simple variant of lokinet_inbound_stream_filter that maps port to localhost:port - int EXPORT - lokinet_inbound_stream(uint16_t port, struct lokinet_context* context); - - void EXPORT - lokinet_close_stream(int stream_id, struct lokinet_context* context); + /// the result of a lokinet stream mapping attempt + struct lokinet_stream_result + { + /// set to zero on success otherwise the error that happened + /// use strerror(3) to get printable string of this error + int error; + + /// the local ip address we mapped the remote endpoint to + /// null terminated + char local_address[256]; + /// the local port we mapped the remote endpoint to + int local_port; + /// the id of the stream we created + int stream_id; + }; + + /// connect out to a remote endpoint + /// remoteAddr is in the form of "name:port" + /// localAddr is either NULL for any or in the form of "ip:port" to bind to an explicit address + void EXPORT lokinet_outbound_stream( + struct lokinet_stream_result* result, + const char* remoteAddr, + const char* localAddr, + struct lokinet_context* context); + + /// stream accept filter determines if we should accept a stream or not + /// return 0 to accept + /// return -1 to explicitly reject + /// return -2 to silently drop + typedef int (*lokinet_stream_filter)(const char* remote, uint16_t port, void* userdata); + + /// set stream accepter filter + /// passes user parameter into stream filter as void * + /// returns stream id + int EXPORT lokinet_inbound_stream_filter( + lokinet_stream_filter acceptFilter, void* user, struct lokinet_context* context); + + /// simple stream acceptor + /// simple variant of lokinet_inbound_stream_filter that maps port to localhost:port + int EXPORT lokinet_inbound_stream(uint16_t port, struct lokinet_context* context); + + void EXPORT lokinet_close_stream(int stream_id, struct lokinet_context* context); #ifdef __cplusplus } diff --git a/include/lokinet/lokinet_udp.h b/include/lokinet/lokinet_udp.h index 9e520dcd3..8346da503 100644 --- a/include/lokinet/lokinet_udp.h +++ b/include/lokinet/lokinet_udp.h @@ -7,115 +7,111 @@ extern "C" { #endif - /// information about a udp flow - struct lokinet_udp_flowinfo - { - /// remote endpoint's .loki or .snode address - char remote_host[256]; - /// remote endpont's port - uint16_t remote_port; - /// the socket id for this flow used for i/o purposes and closing this socket - int socket_id; - }; + /// information about a udp flow + struct lokinet_udp_flowinfo + { + /// remote endpoint's .loki or .snode address + char remote_host[256]; + /// remote endpont's port + uint16_t remote_port; + /// the socket id for this flow used for i/o purposes and closing this socket + int socket_id; + }; - /// a result from a lokinet_udp_bind call - struct lokinet_udp_bind_result - { - /// a socket id used to close a lokinet udp socket - int socket_id; - }; + /// a result from a lokinet_udp_bind call + struct lokinet_udp_bind_result + { + /// a socket id used to close a lokinet udp socket + int socket_id; + }; - /// flow acceptor hook, return 0 success, return nonzero with errno on failure - typedef int (*lokinet_udp_flow_filter)( - void* userdata, - const struct lokinet_udp_flowinfo* remote_address, - void** flow_userdata, - int* timeout_seconds); + /// flow acceptor hook, return 0 success, return nonzero with errno on failure + typedef int (*lokinet_udp_flow_filter)( + void* userdata, + const struct lokinet_udp_flowinfo* remote_address, + void** flow_userdata, + int* timeout_seconds); - /// callback to make a new outbound flow - typedef void(lokinet_udp_create_flow_func)( - void* userdata, void** flow_userdata, int* timeout_seconds); + /// callback to make a new outbound flow + typedef void(lokinet_udp_create_flow_func)( + void* userdata, void** flow_userdata, int* timeout_seconds); - /// hook function for handling packets - typedef void (*lokinet_udp_flow_recv_func)( - const struct lokinet_udp_flowinfo* remote_address, - const char* pkt_data, - size_t pkt_length, - void* flow_userdata); + /// hook function for handling packets + typedef void (*lokinet_udp_flow_recv_func)( + const struct lokinet_udp_flowinfo* remote_address, + const char* pkt_data, + size_t pkt_length, + void* flow_userdata); - /// hook function for flow timeout - typedef void (*lokinet_udp_flow_timeout_func)( - const struct lokinet_udp_flowinfo* remote_address, void* flow_userdata); + /// hook function for flow timeout + typedef void (*lokinet_udp_flow_timeout_func)( + const struct lokinet_udp_flowinfo* remote_address, void* flow_userdata); - /// inbound listen udp socket - /// expose udp port exposePort to the void - //// - /// @param filter MUST be non null, pointing to a flow filter for accepting new udp flows, called - /// with user data - /// - /// @param recv MUST be non null, pointing to a packet handler function for each flow, called - /// with per flow user data provided by filter function if accepted - /// - /// @param timeout MUST be non null, - /// pointing to a cleanup function to clean up a stale flow, staleness determined by the value - /// given by the filter function returns 0 on success - /// - /// @returns nonzero on error in which it is an errno value - int EXPORT - lokinet_udp_bind( - uint16_t exposedPort, - lokinet_udp_flow_filter filter, - lokinet_udp_flow_recv_func recv, - lokinet_udp_flow_timeout_func timeout, - void* user, - struct lokinet_udp_bind_result* result, - struct lokinet_context* ctx); + /// inbound listen udp socket + /// expose udp port exposePort to the void + //// + /// @param filter MUST be non null, pointing to a flow filter for accepting new udp flows, + /// called with user data + /// + /// @param recv MUST be non null, pointing to a packet handler function for each flow, called + /// with per flow user data provided by filter function if accepted + /// + /// @param timeout MUST be non null, + /// pointing to a cleanup function to clean up a stale flow, staleness determined by the value + /// given by the filter function returns 0 on success + /// + /// @returns nonzero on error in which it is an errno value + int EXPORT lokinet_udp_bind( + uint16_t exposedPort, + lokinet_udp_flow_filter filter, + lokinet_udp_flow_recv_func recv, + lokinet_udp_flow_timeout_func timeout, + void* user, + struct lokinet_udp_bind_result* result, + struct lokinet_context* ctx); - /// @brief establish a udp flow to remote endpoint - /// - /// @param create_flow the callback to create the new flow if we establish one - /// - /// @param user passed to new_flow as user data - /// - /// @param remote the remote address to establish to - /// - /// @param ctx the lokinet context to use - /// - /// @return 0 on success, non zero errno on fail - int EXPORT - lokinet_udp_establish( - lokinet_udp_create_flow_func create_flow, - void* user, - const struct lokinet_udp_flowinfo* remote, - struct lokinet_context* ctx); + /// @brief establish a udp flow to remote endpoint + /// + /// @param create_flow the callback to create the new flow if we establish one + /// + /// @param user passed to new_flow as user data + /// + /// @param remote the remote address to establish to + /// + /// @param ctx the lokinet context to use + /// + /// @return 0 on success, non zero errno on fail + int EXPORT lokinet_udp_establish( + lokinet_udp_create_flow_func create_flow, + void* user, + const struct lokinet_udp_flowinfo* remote, + struct lokinet_context* ctx); - /// @brief send on an established flow to remote endpoint - /// blocks until we have sent the packet - /// - /// @param flowinfo remote flow to use for sending - /// - /// @param ptr pointer to data to send - /// - /// @param len the length of the data - /// - /// @param ctx the lokinet context to use - /// - /// @returns 0 on success and non zero errno on fail - int EXPORT - lokinet_udp_flow_send( - const struct lokinet_udp_flowinfo* remote, - const void* ptr, - size_t len, - struct lokinet_context* ctx); + /// @brief send on an established flow to remote endpoint + /// blocks until we have sent the packet + /// + /// @param flowinfo remote flow to use for sending + /// + /// @param ptr pointer to data to send + /// + /// @param len the length of the data + /// + /// @param ctx the lokinet context to use + /// + /// @returns 0 on success and non zero errno on fail + int EXPORT lokinet_udp_flow_send( + const struct lokinet_udp_flowinfo* remote, + const void* ptr, + size_t len, + struct lokinet_context* ctx); - /// @brief close a bound udp socket - /// closes all flows immediately - /// - /// @param socket_id the bound udp socket's id - /// - /// @param ctx lokinet context - void EXPORT - lokinet_udp_close(int socket_id, struct lokinet_context* ctx); + /// @brief close a bound udp socket + /// closes all flows immediately + /// + /// @param socket_id the bound udp socket's id + /// + /// @param ctx lokinet context + void EXPORT lokinet_udp_close(int socket_id, struct lokinet_context* ctx); #ifdef __cplusplus } diff --git a/jni/lokinet_config.cpp b/jni/lokinet_config.cpp index 09489f1a5..b92d9b347 100644 --- a/jni/lokinet_config.cpp +++ b/jni/lokinet_config.cpp @@ -6,70 +6,69 @@ extern "C" { - JNIEXPORT jobject JNICALL - Java_network_loki_lokinet_LokinetConfig_Obtain(JNIEnv* env, jclass, jstring dataDir) - { - auto conf = VisitStringAsStringView( - env, dataDir, [](std::string_view val) -> llarp::Config* { - return new llarp::Config{val}; - }); - - if (conf == nullptr) - return nullptr; - return env->NewDirectByteBuffer(conf, sizeof(llarp::Config)); - } + JNIEXPORT jobject JNICALL + Java_network_loki_lokinet_LokinetConfig_Obtain(JNIEnv* env, jclass, jstring dataDir) + { + auto conf = VisitStringAsStringView( + env, dataDir, [](std::string_view val) -> llarp::Config* { + return new llarp::Config{val}; + }); - JNIEXPORT void JNICALL - Java_network_loki_lokinet_LokinetConfig_Free(JNIEnv* env, jclass, jobject buf) - { - auto ptr = FromBuffer(env, buf); - delete ptr; - } + if (conf == nullptr) + return nullptr; + return env->NewDirectByteBuffer(conf, sizeof(llarp::Config)); + } - JNIEXPORT jboolean JNICALL - Java_network_loki_lokinet_LokinetConfig_Load(JNIEnv* env, jobject self) - { - auto conf = GetImpl(env, self); - if (conf == nullptr) - return JNI_FALSE; - if (conf->Load()) + JNIEXPORT void JNICALL + Java_network_loki_lokinet_LokinetConfig_Free(JNIEnv* env, jclass, jobject buf) { - return JNI_TRUE; + auto ptr = FromBuffer(env, buf); + delete ptr; } - return JNI_FALSE; - } - JNIEXPORT jboolean JNICALL - Java_network_loki_lokinet_LokinetConfig_Save(JNIEnv* env, jobject self) - { - auto conf = GetImpl(env, self); - if (conf == nullptr) - return JNI_FALSE; - try + JNIEXPORT jboolean JNICALL + Java_network_loki_lokinet_LokinetConfig_Load(JNIEnv* env, jobject self) { - conf->Save(); + auto conf = GetImpl(env, self); + if (conf == nullptr) + return JNI_FALSE; + if (conf->Load()) + { + return JNI_TRUE; + } + return JNI_FALSE; } - catch (...) + + JNIEXPORT jboolean JNICALL + Java_network_loki_lokinet_LokinetConfig_Save(JNIEnv* env, jobject self) { - return JNI_FALSE; + auto conf = GetImpl(env, self); + if (conf == nullptr) + return JNI_FALSE; + try + { + conf->Save(); + } + catch (...) + { + return JNI_FALSE; + } + return JNI_TRUE; } - return JNI_TRUE; - } - JNIEXPORT void JNICALL - Java_network_loki_lokinet_LokinetConfig_AddDefaultValue( - JNIEnv* env, jobject self, jstring section, jstring key, jstring value) - { - auto convert = [](std::string_view str) -> std::string { return std::string{str}; }; + JNIEXPORT void JNICALL Java_network_loki_lokinet_LokinetConfig_AddDefaultValue( + JNIEnv* env, jobject self, jstring section, jstring key, jstring value) + { + auto convert = [](std::string_view str) -> std::string { return std::string{str}; }; - const auto sect = VisitStringAsStringView(env, section, convert); - const auto k = VisitStringAsStringView(env, key, convert); - const auto v = VisitStringAsStringView(env, value, convert); + const auto sect = VisitStringAsStringView(env, section, convert); + const auto k = VisitStringAsStringView(env, key, convert); + const auto v = VisitStringAsStringView(env, value, convert); - auto conf = GetImpl(env, self); - if (conf) - { - conf->AddDefault(sect, k, v); + auto conf = GetImpl(env, self); + if (conf) + { + conf->AddDefault(sect, k, v); + } } - } } diff --git a/jni/lokinet_daemon.cpp b/jni/lokinet_daemon.cpp index 92e13d832..4fb0c162c 100644 --- a/jni/lokinet_daemon.cpp +++ b/jni/lokinet_daemon.cpp @@ -7,116 +7,115 @@ extern "C" { - JNIEXPORT jobject JNICALL - Java_network_loki_lokinet_LokinetDaemon_Obtain(JNIEnv* env, jclass) - { - auto* ptr = new llarp::Context(); - if (ptr == nullptr) - return nullptr; - return env->NewDirectByteBuffer(ptr, sizeof(llarp::Context)); - } + JNIEXPORT jobject JNICALL Java_network_loki_lokinet_LokinetDaemon_Obtain(JNIEnv* env, jclass) + { + auto* ptr = new llarp::Context(); + if (ptr == nullptr) + return nullptr; + return env->NewDirectByteBuffer(ptr, sizeof(llarp::Context)); + } - JNIEXPORT void JNICALL - Java_network_loki_lokinet_LokinetDaemon_Free(JNIEnv* env, jclass, jobject buf) - { - auto ptr = FromBuffer(env, buf); - delete ptr; - } + JNIEXPORT void JNICALL + Java_network_loki_lokinet_LokinetDaemon_Free(JNIEnv* env, jclass, jobject buf) + { + auto ptr = FromBuffer(env, buf); + delete ptr; + } - JNIEXPORT jboolean JNICALL - Java_network_loki_lokinet_LokinetDaemon_Configure(JNIEnv* env, jobject self, jobject conf) - { - auto ptr = GetImpl(env, self); - auto config = GetImpl(env, conf); - if (ptr == nullptr || config == nullptr) - return JNI_FALSE; - try + JNIEXPORT jboolean JNICALL + Java_network_loki_lokinet_LokinetDaemon_Configure(JNIEnv* env, jobject self, jobject conf) { - llarp::RuntimeOptions opts{}; + auto ptr = GetImpl(env, self); + auto config = GetImpl(env, conf); + if (ptr == nullptr || config == nullptr) + return JNI_FALSE; + try + { + llarp::RuntimeOptions opts{}; - // janky make_shared deep copy because jni + shared pointer = scary - ptr->Configure(std::make_shared(*config)); - ptr->Setup(opts); + // janky make_shared deep copy because jni + shared pointer = scary + ptr->Configure(std::make_shared(*config)); + ptr->Setup(opts); + } + catch (...) + { + return JNI_FALSE; + } + return JNI_TRUE; } - catch (...) + + JNIEXPORT jint JNICALL + Java_network_loki_lokinet_LokinetDaemon_Mainloop(JNIEnv* env, jobject self) { - return JNI_FALSE; + auto ptr = GetImpl(env, self); + if (ptr == nullptr) + return -1; + llarp::RuntimeOptions opts{}; + return ptr->Run(opts); } - return JNI_TRUE; - } - - JNIEXPORT jint JNICALL - Java_network_loki_lokinet_LokinetDaemon_Mainloop(JNIEnv* env, jobject self) - { - auto ptr = GetImpl(env, self); - if (ptr == nullptr) - return -1; - llarp::RuntimeOptions opts{}; - return ptr->Run(opts); - } - JNIEXPORT jboolean JNICALL - Java_network_loki_lokinet_LokinetDaemon_IsRunning(JNIEnv* env, jobject self) - { - auto ptr = GetImpl(env, self); - return (ptr != nullptr && ptr->IsUp()) ? JNI_TRUE : JNI_FALSE; - } + JNIEXPORT jboolean JNICALL + Java_network_loki_lokinet_LokinetDaemon_IsRunning(JNIEnv* env, jobject self) + { + auto ptr = GetImpl(env, self); + return (ptr != nullptr && ptr->IsUp()) ? JNI_TRUE : JNI_FALSE; + } - JNIEXPORT jboolean JNICALL - Java_network_loki_lokinet_LokinetDaemon_Stop(JNIEnv* env, jobject self) - { - auto ptr = GetImpl(env, self); - if (ptr == nullptr) - return JNI_FALSE; - if (not ptr->IsUp()) - return JNI_FALSE; - ptr->CloseAsync(); - ptr->Wait(); - return ptr->IsUp() ? JNI_FALSE : JNI_TRUE; - } + JNIEXPORT jboolean JNICALL + Java_network_loki_lokinet_LokinetDaemon_Stop(JNIEnv* env, jobject self) + { + auto ptr = GetImpl(env, self); + if (ptr == nullptr) + return JNI_FALSE; + if (not ptr->IsUp()) + return JNI_FALSE; + ptr->CloseAsync(); + ptr->Wait(); + return ptr->IsUp() ? JNI_FALSE : JNI_TRUE; + } - JNIEXPORT void JNICALL - Java_network_loki_lokinet_LokinetDaemon_InjectVPNFD(JNIEnv* env, jobject self) - { - if (auto ptr = GetImpl(env, self)) - ptr->androidFD = GetObjectMemberAsInt(env, self, "m_FD"); - } + JNIEXPORT void JNICALL + Java_network_loki_lokinet_LokinetDaemon_InjectVPNFD(JNIEnv* env, jobject self) + { + if (auto ptr = GetImpl(env, self)) + ptr->androidFD = GetObjectMemberAsInt(env, self, "m_FD"); + } - JNIEXPORT jint JNICALL - Java_network_loki_lokinet_LokinetDaemon_GetUDPSocket(JNIEnv* env, jobject self) - { - if (auto ptr = GetImpl(env, self); ptr and ptr->router) - return ptr->router->outbound_socket(); - return -1; - } + JNIEXPORT jint JNICALL + Java_network_loki_lokinet_LokinetDaemon_GetUDPSocket(JNIEnv* env, jobject self) + { + if (auto ptr = GetImpl(env, self); ptr and ptr->router) + return ptr->router->outbound_socket(); + return -1; + } - JNIEXPORT jstring JNICALL - Java_network_loki_lokinet_LokinetDaemon_DetectFreeRange(JNIEnv* env, jclass) - { - std::string rangestr{}; - if (auto maybe = llarp::net::Platform::Default_ptr()->FindFreeRange()) + JNIEXPORT jstring JNICALL + Java_network_loki_lokinet_LokinetDaemon_DetectFreeRange(JNIEnv* env, jclass) { - rangestr = maybe->ToString(); + std::string rangestr{}; + if (auto maybe = llarp::net::Platform::Default_ptr()->FindFreeRange()) + { + rangestr = maybe->ToString(); + } + return env->NewStringUTF(rangestr.c_str()); } - return env->NewStringUTF(rangestr.c_str()); - } - JNIEXPORT jstring JNICALL - Java_network_loki_lokinet_LokinetDaemon_DumpStatus(JNIEnv* env, jobject self) - { - std::string status{}; - if (auto ptr = GetImpl(env, self)) + JNIEXPORT jstring JNICALL + Java_network_loki_lokinet_LokinetDaemon_DumpStatus(JNIEnv* env, jobject self) { - if (ptr->IsUp()) - { - std::promise result; - ptr->CallSafe([&result, router = ptr->router]() { - const auto status = router->ExtractStatus(); - result.set_value(status.dump()); - }); - status = result.get_future().get(); - } + std::string status{}; + if (auto ptr = GetImpl(env, self)) + { + if (ptr->IsUp()) + { + std::promise result; + ptr->CallSafe([&result, router = ptr->router]() { + const auto status = router->ExtractStatus(); + result.set_value(status.dump()); + }); + status = result.get_future().get(); + } + } + return env->NewStringUTF(status.c_str()); } - return env->NewStringUTF(status.c_str()); - } } diff --git a/jni/lokinet_jni_common.hpp b/jni/lokinet_jni_common.hpp index 0a8452de8..443983be8 100644 --- a/jni/lokinet_jni_common.hpp +++ b/jni/lokinet_jni_common.hpp @@ -8,73 +8,67 @@ /// visit string as native bytes /// jvm uses some unholy encoding internally so we convert it to utf-8 template -static T -VisitStringAsStringView(JNIEnv* env, jobject str, V visit) +static T VisitStringAsStringView(JNIEnv* env, jobject str, V visit) { - const jclass stringClass = env->GetObjectClass(str); - const jmethodID getBytes = env->GetMethodID(stringClass, "getBytes", "(Ljava/lang/String;)[B"); + const jclass stringClass = env->GetObjectClass(str); + const jmethodID getBytes = env->GetMethodID(stringClass, "getBytes", "(Ljava/lang/String;)[B"); - const jstring charsetName = env->NewStringUTF("UTF-8"); - const jbyteArray stringJbytes = (jbyteArray)env->CallObjectMethod(str, getBytes, charsetName); - env->DeleteLocalRef(charsetName); + const jstring charsetName = env->NewStringUTF("UTF-8"); + const jbyteArray stringJbytes = (jbyteArray)env->CallObjectMethod(str, getBytes, charsetName); + env->DeleteLocalRef(charsetName); - const size_t length = env->GetArrayLength(stringJbytes); - jbyte* pBytes = env->GetByteArrayElements(stringJbytes, NULL); + const size_t length = env->GetArrayLength(stringJbytes); + jbyte* pBytes = env->GetByteArrayElements(stringJbytes, NULL); - T result = visit(std::string_view((const char*)pBytes, length)); + T result = visit(std::string_view((const char*)pBytes, length)); - env->ReleaseByteArrayElements(stringJbytes, pBytes, JNI_ABORT); - env->DeleteLocalRef(stringJbytes); + env->ReleaseByteArrayElements(stringJbytes, pBytes, JNI_ABORT); + env->DeleteLocalRef(stringJbytes); - return result; + return result; } /// cast jni buffer to T * template -static T* -FromBuffer(JNIEnv* env, jobject o) +static T* FromBuffer(JNIEnv* env, jobject o) { - if (o == nullptr) - return nullptr; - return static_cast(env->GetDirectBufferAddress(o)); + if (o == nullptr) + return nullptr; + return static_cast(env->GetDirectBufferAddress(o)); } /// get T * from object member called membername template -static T* -FromObjectMember(JNIEnv* env, jobject self, const char* membername) +static T* FromObjectMember(JNIEnv* env, jobject self, const char* membername) { - jclass cl = env->GetObjectClass(self); - jfieldID name = env->GetFieldID(cl, membername, "Ljava/nio/ByteBuffer;"); - jobject buffer = env->GetObjectField(self, name); - return FromBuffer(env, buffer); + jclass cl = env->GetObjectClass(self); + jfieldID name = env->GetFieldID(cl, membername, "Ljava/nio/ByteBuffer;"); + jobject buffer = env->GetObjectField(self, name); + return FromBuffer(env, buffer); } /// visit object string member called membername as bytes template -static T -VisitObjectMemberStringAsStringView(JNIEnv* env, jobject self, const char* membername, V v) +static T VisitObjectMemberStringAsStringView(JNIEnv* env, jobject self, const char* membername, V v) { - jclass cl = env->GetObjectClass(self); - jfieldID name = env->GetFieldID(cl, membername, "Ljava/lang/String;"); - jobject str = env->GetObjectField(self, name); - return VisitStringAsStringView(env, str, v); + jclass cl = env->GetObjectClass(self); + jfieldID name = env->GetFieldID(cl, membername, "Ljava/lang/String;"); + jobject str = env->GetObjectField(self, name); + return VisitStringAsStringView(env, str, v); } /// get object member int called membername template -Int_t -GetObjectMemberAsInt(JNIEnv* env, jobject self, const char* membername) +Int_t GetObjectMemberAsInt(JNIEnv* env, jobject self, const char* membername) { - jclass cl = env->GetObjectClass(self); - jfieldID name = env->GetFieldID(cl, membername, "I"); - return env->GetIntField(self, name); + jclass cl = env->GetObjectClass(self); + jfieldID name = env->GetFieldID(cl, membername, "I"); + return env->GetIntField(self, name); } /// get implementation on jni type template -T* -GetImpl(JNIEnv* env, jobject self) +T* GetImpl(JNIEnv* env, jobject self) { - return FromObjectMember(env, self, "impl"); + return FromObjectMember(env, self, "impl"); } diff --git a/jni/network_loki_lokinet_LokinetConfig.h b/jni/network_loki_lokinet_LokinetConfig.h index 341a64fdd..4dca70b12 100644 --- a/jni/network_loki_lokinet_LokinetConfig.h +++ b/jni/network_loki_lokinet_LokinetConfig.h @@ -8,46 +8,42 @@ extern "C" { #endif - /* - * Class: network_loki_lokinet_LokinetConfig - * Method: Obtain - * Signature: (Ljava/lang/String;)Ljava/nio/ByteBuffer; - */ - JNIEXPORT jobject JNICALL - Java_network_loki_lokinet_LokinetConfig_Obtain(JNIEnv*, jclass, jstring); + /* + * Class: network_loki_lokinet_LokinetConfig + * Method: Obtain + * Signature: (Ljava/lang/String;)Ljava/nio/ByteBuffer; + */ + JNIEXPORT jobject JNICALL + Java_network_loki_lokinet_LokinetConfig_Obtain(JNIEnv*, jclass, jstring); - /* - * Class: network_loki_lokinet_LokinetConfig - * Method: Free - * Signature: (Ljava/nio/ByteBuffer;)V - */ - JNIEXPORT void JNICALL - Java_network_loki_lokinet_LokinetConfig_Free(JNIEnv*, jclass, jobject); + /* + * Class: network_loki_lokinet_LokinetConfig + * Method: Free + * Signature: (Ljava/nio/ByteBuffer;)V + */ + JNIEXPORT void JNICALL Java_network_loki_lokinet_LokinetConfig_Free(JNIEnv*, jclass, jobject); - /* - * Class: network_loki_lokinet_LokinetConfig - * Method: Load - * Signature: ()Z - */ - JNIEXPORT jboolean JNICALL - Java_network_loki_lokinet_LokinetConfig_Load(JNIEnv*, jobject); + /* + * Class: network_loki_lokinet_LokinetConfig + * Method: Load + * Signature: ()Z + */ + JNIEXPORT jboolean JNICALL Java_network_loki_lokinet_LokinetConfig_Load(JNIEnv*, jobject); - /* - * Class: network_loki_lokinet_LokinetConfig - * Method: Save - * Signature: ()Z - */ - JNIEXPORT jboolean JNICALL - Java_network_loki_lokinet_LokinetConfig_Save(JNIEnv*, jobject); + /* + * Class: network_loki_lokinet_LokinetConfig + * Method: Save + * Signature: ()Z + */ + JNIEXPORT jboolean JNICALL Java_network_loki_lokinet_LokinetConfig_Save(JNIEnv*, jobject); - /* - * Class: network_loki_lokinet_LokinetConfig - * Method: AddDefaultValue - * Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V - */ - JNIEXPORT void JNICALL - Java_network_loki_lokinet_LokinetConfig_AddDefaultValue( - JNIEnv*, jobject, jstring, jstring, jstring); + /* + * Class: network_loki_lokinet_LokinetConfig + * Method: AddDefaultValue + * Signature: (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V + */ + JNIEXPORT void JNICALL Java_network_loki_lokinet_LokinetConfig_AddDefaultValue( + JNIEnv*, jobject, jstring, jstring, jstring); #ifdef __cplusplus } diff --git a/jni/network_loki_lokinet_LokinetDaemon.h b/jni/network_loki_lokinet_LokinetDaemon.h index cc4d54fed..ca7b44e49 100644 --- a/jni/network_loki_lokinet_LokinetDaemon.h +++ b/jni/network_loki_lokinet_LokinetDaemon.h @@ -8,85 +8,77 @@ extern "C" { #endif - /* - * Class: network_loki_lokinet_LokinetDaemon - * Method: Obtain - * Signature: ()Ljava/nio/ByteBuffer; - */ - JNIEXPORT jobject JNICALL - Java_network_loki_lokinet_LokinetDaemon_Obtain(JNIEnv*, jclass); + /* + * Class: network_loki_lokinet_LokinetDaemon + * Method: Obtain + * Signature: ()Ljava/nio/ByteBuffer; + */ + JNIEXPORT jobject JNICALL Java_network_loki_lokinet_LokinetDaemon_Obtain(JNIEnv*, jclass); - /* - * Class: network_loki_lokinet_LokinetDaemon - * Method: Free - * Signature: (Ljava/nio/ByteBuffer;)V - */ - JNIEXPORT void JNICALL - Java_network_loki_lokinet_LokinetDaemon_Free(JNIEnv*, jclass, jobject); + /* + * Class: network_loki_lokinet_LokinetDaemon + * Method: Free + * Signature: (Ljava/nio/ByteBuffer;)V + */ + JNIEXPORT void JNICALL Java_network_loki_lokinet_LokinetDaemon_Free(JNIEnv*, jclass, jobject); - /* - * Class: network_loki_lokinet_LokinetDaemon - * Method: Configure - * Signature: (Lnetwork/loki/lokinet/LokinetConfig;)Z - */ - JNIEXPORT jboolean JNICALL - Java_network_loki_lokinet_LokinetDaemon_Configure(JNIEnv*, jobject, jobject); + /* + * Class: network_loki_lokinet_LokinetDaemon + * Method: Configure + * Signature: (Lnetwork/loki/lokinet/LokinetConfig;)Z + */ + JNIEXPORT jboolean JNICALL + Java_network_loki_lokinet_LokinetDaemon_Configure(JNIEnv*, jobject, jobject); - /* - * Class: network_loki_lokinet_LokinetDaemon - * Method: Mainloop - * Signature: ()I - */ - JNIEXPORT jint JNICALL - Java_network_loki_lokinet_LokinetDaemon_Mainloop(JNIEnv*, jobject); + /* + * Class: network_loki_lokinet_LokinetDaemon + * Method: Mainloop + * Signature: ()I + */ + JNIEXPORT jint JNICALL Java_network_loki_lokinet_LokinetDaemon_Mainloop(JNIEnv*, jobject); - /* - * Class: network_loki_lokinet_LokinetDaemon - * Method: IsRunning - * Signature: ()Z - */ - JNIEXPORT jboolean JNICALL - Java_network_loki_lokinet_LokinetDaemon_IsRunning(JNIEnv*, jobject); + /* + * Class: network_loki_lokinet_LokinetDaemon + * Method: IsRunning + * Signature: ()Z + */ + JNIEXPORT jboolean JNICALL Java_network_loki_lokinet_LokinetDaemon_IsRunning(JNIEnv*, jobject); - /* - * Class: network_loki_lokinet_LokinetDaemon - * Method: Stop - * Signature: ()Z - */ - JNIEXPORT jboolean JNICALL - Java_network_loki_lokinet_LokinetDaemon_Stop(JNIEnv*, jobject); + /* + * Class: network_loki_lokinet_LokinetDaemon + * Method: Stop + * Signature: ()Z + */ + JNIEXPORT jboolean JNICALL Java_network_loki_lokinet_LokinetDaemon_Stop(JNIEnv*, jobject); - /* - * Class: network_loki_lokinet_LokinetDaemon - * Method: InjectVPNFD - * Signature: ()V - */ - JNIEXPORT void JNICALL - Java_network_loki_lokinet_LokinetDaemon_InjectVPNFD(JNIEnv*, jobject); + /* + * Class: network_loki_lokinet_LokinetDaemon + * Method: InjectVPNFD + * Signature: ()V + */ + JNIEXPORT void JNICALL Java_network_loki_lokinet_LokinetDaemon_InjectVPNFD(JNIEnv*, jobject); - /* - * Class: network_loki_lokinet_LokinetDaemon - * Method: GetUDPSocket - * Signature: ()I - */ - JNIEXPORT jint JNICALL - Java_network_loki_lokinet_LokinetDaemon_GetUDPSocket(JNIEnv*, jobject); + /* + * Class: network_loki_lokinet_LokinetDaemon + * Method: GetUDPSocket + * Signature: ()I + */ + JNIEXPORT jint JNICALL Java_network_loki_lokinet_LokinetDaemon_GetUDPSocket(JNIEnv*, jobject); - /* - * Class: network_loki_lokinet_LokinetDaemon - * Method: DetectFreeRange - * Signature: ()Ljava/lang/String; - */ - JNIEXPORT jstring JNICALL - Java_network_loki_lokinet_LokinetDaemon_DetectFreeRange(JNIEnv*, jclass); + /* + * Class: network_loki_lokinet_LokinetDaemon + * Method: DetectFreeRange + * Signature: ()Ljava/lang/String; + */ + JNIEXPORT jstring JNICALL + Java_network_loki_lokinet_LokinetDaemon_DetectFreeRange(JNIEnv*, jclass); - /* - * Class: network_loki_lokinet_LokinetDaemon - * Method: DumpStatus - * Signature: ()Ljava/lang/String; - */ - JNIEXPORT jstring JNICALL - Java_network_loki_lokinet_LokinetDaemon_DumpStatus(JNIEnv*, jobject); + /* + * Class: network_loki_lokinet_LokinetDaemon + * Method: DumpStatus + * Signature: ()Ljava/lang/String; + */ + JNIEXPORT jstring JNICALL Java_network_loki_lokinet_LokinetDaemon_DumpStatus(JNIEnv*, jobject); #ifdef __cplusplus } diff --git a/jni/network_loki_lokinet_LokinetVPN.h b/jni/network_loki_lokinet_LokinetVPN.h index a3a56a8e4..0b8d3ebfb 100644 --- a/jni/network_loki_lokinet_LokinetVPN.h +++ b/jni/network_loki_lokinet_LokinetVPN.h @@ -8,61 +8,55 @@ extern "C" { #endif - /* - * Class: network_loki_lokinet_LokinetVPN - * Method: PacketSize - * Signature: ()I - */ - JNIEXPORT jint JNICALL - Java_network_loki_lokinet_LokinetVPN_PacketSize(JNIEnv*, jclass); + /* + * Class: network_loki_lokinet_LokinetVPN + * Method: PacketSize + * Signature: ()I + */ + JNIEXPORT jint JNICALL Java_network_loki_lokinet_LokinetVPN_PacketSize(JNIEnv*, jclass); - /* - * Class: network_loki_lokinet_LokinetVPN - * Method: Alloc - * Signature: ()Ljava/nio/Buffer; - */ - JNIEXPORT jobject JNICALL - Java_network_loki_lokinet_LokinetVPN_Alloc(JNIEnv*, jclass); + /* + * Class: network_loki_lokinet_LokinetVPN + * Method: Alloc + * Signature: ()Ljava/nio/Buffer; + */ + JNIEXPORT jobject JNICALL Java_network_loki_lokinet_LokinetVPN_Alloc(JNIEnv*, jclass); - /* - * Class: network_loki_lokinet_LokinetVPN - * Method: Free - * Signature: (Ljava/nio/Buffer;)V - */ - JNIEXPORT void JNICALL - Java_network_loki_lokinet_LokinetVPN_Free(JNIEnv*, jclass, jobject); + /* + * Class: network_loki_lokinet_LokinetVPN + * Method: Free + * Signature: (Ljava/nio/Buffer;)V + */ + JNIEXPORT void JNICALL Java_network_loki_lokinet_LokinetVPN_Free(JNIEnv*, jclass, jobject); - /* - * Class: network_loki_lokinet_LokinetVPN - * Method: Stop - * Signature: ()V - */ - JNIEXPORT void JNICALL - Java_network_loki_lokinet_LokinetVPN_Stop(JNIEnv*, jobject); + /* + * Class: network_loki_lokinet_LokinetVPN + * Method: Stop + * Signature: ()V + */ + JNIEXPORT void JNICALL Java_network_loki_lokinet_LokinetVPN_Stop(JNIEnv*, jobject); - /* - * Class: network_loki_lokinet_LokinetVPN - * Method: ReadPkt - * Signature: (Ljava/nio/ByteBuffer;)I - */ - JNIEXPORT jint JNICALL - Java_network_loki_lokinet_LokinetVPN_ReadPkt(JNIEnv*, jobject, jobject); + /* + * Class: network_loki_lokinet_LokinetVPN + * Method: ReadPkt + * Signature: (Ljava/nio/ByteBuffer;)I + */ + JNIEXPORT jint JNICALL Java_network_loki_lokinet_LokinetVPN_ReadPkt(JNIEnv*, jobject, jobject); - /* - * Class: network_loki_lokinet_LokinetVPN - * Method: WritePkt - * Signature: (Ljava/nio/ByteBuffer;)Z - */ - JNIEXPORT jboolean JNICALL - Java_network_loki_lokinet_LokinetVPN_WritePkt(JNIEnv*, jobject, jobject); + /* + * Class: network_loki_lokinet_LokinetVPN + * Method: WritePkt + * Signature: (Ljava/nio/ByteBuffer;)Z + */ + JNIEXPORT jboolean JNICALL + Java_network_loki_lokinet_LokinetVPN_WritePkt(JNIEnv*, jobject, jobject); - /* - * Class: network_loki_lokinet_LokinetVPN - * Method: SetInfo - * Signature: (Lnetwork/loki/lokinet/LokinetVPN/VPNInfo;)V - */ - JNIEXPORT void JNICALL - Java_network_loki_lokinet_LokinetVPN_SetInfo(JNIEnv*, jobject, jobject); + /* + * Class: network_loki_lokinet_LokinetVPN + * Method: SetInfo + * Signature: (Lnetwork/loki/lokinet/LokinetVPN/VPNInfo;)V + */ + JNIEXPORT void JNICALL Java_network_loki_lokinet_LokinetVPN_SetInfo(JNIEnv*, jobject, jobject); #ifdef __cplusplus } #endif diff --git a/jni/network_loki_lokinet_Lokinet_JNI.h b/jni/network_loki_lokinet_Lokinet_JNI.h index c94eed445..87ddde147 100644 --- a/jni/network_loki_lokinet_Lokinet_JNI.h +++ b/jni/network_loki_lokinet_Lokinet_JNI.h @@ -8,46 +8,43 @@ extern "C" { #endif - /* - * Class: network_loki_lokinet_Lokinet_JNI - * Method: getABICompiledWith - * Signature: ()Ljava/lang/String; - */ - JNIEXPORT jstring JNICALL - Java_network_loki_lokinet_Lokinet_1JNI_getABICompiledWith(JNIEnv*, jclass); - - /* - * Class: network_loki_lokinet_Lokinet_JNI - * Method: startLokinet - * Signature: (Ljava/lang/String;)Ljava/lang/String; - */ - JNIEXPORT jstring JNICALL - Java_network_loki_lokinet_Lokinet_1JNI_startLokinet(JNIEnv*, jclass, jstring); - - JNIEXPORT jstring JNICALL - Java_network_loki_lokinet_Lokinet_1JNI_getIfAddr(JNIEnv*, jclass); - - JNIEXPORT jint JNICALL - Java_network_loki_lokinet_Lokinet_1JNI_getIfRange(JNIEnv*, jclass); - - /* - * Class: network_loki_lokinet_Lokinet_JNI - * Method: stopLokinet - * Signature: ()V - */ - JNIEXPORT void JNICALL - Java_network_loki_lokinet_Lokinet_1JNI_stopLokinet(JNIEnv*, jclass); - - JNIEXPORT void JNICALL - Java_network_loki_lokinet_Lokinet_1JNI_setVPNFileDescriptor(JNIEnv*, jclass, jint, jint); - - /* - * Class: network_loki_lokinet_Lokinet_JNI - * Method: onNetworkStateChanged - * Signature: (Z)V - */ - JNIEXPORT void JNICALL - Java_network_loki_lokinet_Lokinet_1JNI_onNetworkStateChanged(JNIEnv*, jclass, jboolean); + /* + * Class: network_loki_lokinet_Lokinet_JNI + * Method: getABICompiledWith + * Signature: ()Ljava/lang/String; + */ + JNIEXPORT jstring JNICALL + Java_network_loki_lokinet_Lokinet_1JNI_getABICompiledWith(JNIEnv*, jclass); + + /* + * Class: network_loki_lokinet_Lokinet_JNI + * Method: startLokinet + * Signature: (Ljava/lang/String;)Ljava/lang/String; + */ + JNIEXPORT jstring JNICALL + Java_network_loki_lokinet_Lokinet_1JNI_startLokinet(JNIEnv*, jclass, jstring); + + JNIEXPORT jstring JNICALL Java_network_loki_lokinet_Lokinet_1JNI_getIfAddr(JNIEnv*, jclass); + + JNIEXPORT jint JNICALL Java_network_loki_lokinet_Lokinet_1JNI_getIfRange(JNIEnv*, jclass); + + /* + * Class: network_loki_lokinet_Lokinet_JNI + * Method: stopLokinet + * Signature: ()V + */ + JNIEXPORT void JNICALL Java_network_loki_lokinet_Lokinet_1JNI_stopLokinet(JNIEnv*, jclass); + + JNIEXPORT void JNICALL + Java_network_loki_lokinet_Lokinet_1JNI_setVPNFileDescriptor(JNIEnv*, jclass, jint, jint); + + /* + * Class: network_loki_lokinet_Lokinet_JNI + * Method: onNetworkStateChanged + * Signature: (Z)V + */ + JNIEXPORT void JNICALL + Java_network_loki_lokinet_Lokinet_1JNI_onNetworkStateChanged(JNIEnv*, jclass, jboolean); #ifdef __cplusplus } diff --git a/llarp/android/ifaddrs.c b/llarp/android/ifaddrs.c index af0a439e2..a39bb524a 100644 --- a/llarp/android/ifaddrs.c +++ b/llarp/android/ifaddrs.c @@ -38,661 +38,648 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. typedef struct NetlinkList { - struct NetlinkList* m_next; - struct nlmsghdr* m_data; - unsigned int m_size; + struct NetlinkList* m_next; + struct nlmsghdr* m_data; + unsigned int m_size; } NetlinkList; -static int -netlink_socket(void) +static int netlink_socket(void) { - int l_socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - if (l_socket < 0) - { - return -1; - } - - struct sockaddr_nl l_addr; - memset(&l_addr, 0, sizeof(l_addr)); - l_addr.nl_family = AF_NETLINK; - if (bind(l_socket, (struct sockaddr*)&l_addr, sizeof(l_addr)) < 0) - { - close(l_socket); - return -1; - } + int l_socket = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (l_socket < 0) + { + return -1; + } + + struct sockaddr_nl l_addr; + memset(&l_addr, 0, sizeof(l_addr)); + l_addr.nl_family = AF_NETLINK; + if (bind(l_socket, (struct sockaddr*)&l_addr, sizeof(l_addr)) < 0) + { + close(l_socket); + return -1; + } - return l_socket; + return l_socket; } -static int -netlink_send(int p_socket, int p_request) +static int netlink_send(int p_socket, int p_request) { - struct - { - struct nlmsghdr m_hdr; - struct rtgenmsg m_msg; - } l_data; - - memset(&l_data, 0, sizeof(l_data)); - - l_data.m_hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)); - l_data.m_hdr.nlmsg_type = p_request; - l_data.m_hdr.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST; - l_data.m_hdr.nlmsg_pid = 0; - l_data.m_hdr.nlmsg_seq = p_socket; - l_data.m_msg.rtgen_family = AF_UNSPEC; - - struct sockaddr_nl l_addr; - memset(&l_addr, 0, sizeof(l_addr)); - l_addr.nl_family = AF_NETLINK; - return (sendto( - p_socket, - &l_data.m_hdr, - l_data.m_hdr.nlmsg_len, - 0, - (struct sockaddr*)&l_addr, - sizeof(l_addr))); + struct + { + struct nlmsghdr m_hdr; + struct rtgenmsg m_msg; + } l_data; + + memset(&l_data, 0, sizeof(l_data)); + + l_data.m_hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtgenmsg)); + l_data.m_hdr.nlmsg_type = p_request; + l_data.m_hdr.nlmsg_flags = NLM_F_ROOT | NLM_F_MATCH | NLM_F_REQUEST; + l_data.m_hdr.nlmsg_pid = 0; + l_data.m_hdr.nlmsg_seq = p_socket; + l_data.m_msg.rtgen_family = AF_UNSPEC; + + struct sockaddr_nl l_addr; + memset(&l_addr, 0, sizeof(l_addr)); + l_addr.nl_family = AF_NETLINK; + return (sendto( + p_socket, + &l_data.m_hdr, + l_data.m_hdr.nlmsg_len, + 0, + (struct sockaddr*)&l_addr, + sizeof(l_addr))); } -static int -netlink_recv(int p_socket, void* p_buffer, size_t p_len) +static int netlink_recv(int p_socket, void* p_buffer, size_t p_len) { - struct msghdr l_msg; - struct iovec l_iov = {p_buffer, p_len}; - struct sockaddr_nl l_addr; - - for (;;) - { - l_msg.msg_name = (void*)&l_addr; - l_msg.msg_namelen = sizeof(l_addr); - l_msg.msg_iov = &l_iov; - l_msg.msg_iovlen = 1; - l_msg.msg_control = NULL; - l_msg.msg_controllen = 0; - l_msg.msg_flags = 0; - int l_result = recvmsg(p_socket, &l_msg, 0); - - if (l_result < 0) + struct msghdr l_msg; + struct iovec l_iov = {p_buffer, p_len}; + struct sockaddr_nl l_addr; + + for (;;) { - if (errno == EINTR) - { - continue; - } - return -2; - } + l_msg.msg_name = (void*)&l_addr; + l_msg.msg_namelen = sizeof(l_addr); + l_msg.msg_iov = &l_iov; + l_msg.msg_iovlen = 1; + l_msg.msg_control = NULL; + l_msg.msg_controllen = 0; + l_msg.msg_flags = 0; + int l_result = recvmsg(p_socket, &l_msg, 0); + + if (l_result < 0) + { + if (errno == EINTR) + { + continue; + } + return -2; + } - if (l_msg.msg_flags & MSG_TRUNC) - { // buffer was too small - return -1; + if (l_msg.msg_flags & MSG_TRUNC) + { // buffer was too small + return -1; + } + return l_result; } - return l_result; - } } -static struct nlmsghdr* -getNetlinkResponse(int p_socket, int* p_size, int* p_done) +static struct nlmsghdr* getNetlinkResponse(int p_socket, int* p_size, int* p_done) { - size_t l_size = 4096; - void* l_buffer = NULL; - - for (;;) - { - free(l_buffer); - l_buffer = malloc(l_size); - if (l_buffer == NULL) - { - return NULL; - } + size_t l_size = 4096; + void* l_buffer = NULL; - int l_read = netlink_recv(p_socket, l_buffer, l_size); - *p_size = l_read; - if (l_read == -2) + for (;;) { - free(l_buffer); - return NULL; - } - if (l_read >= 0) - { - pid_t l_pid = getpid(); - struct nlmsghdr* l_hdr; - for (l_hdr = (struct nlmsghdr*)l_buffer; NLMSG_OK(l_hdr, (unsigned int)l_read); - l_hdr = (struct nlmsghdr*)NLMSG_NEXT(l_hdr, l_read)) - { - if ((pid_t)l_hdr->nlmsg_pid != l_pid || (int)l_hdr->nlmsg_seq != p_socket) + free(l_buffer); + l_buffer = malloc(l_size); + if (l_buffer == NULL) { - continue; + return NULL; } - if (l_hdr->nlmsg_type == NLMSG_DONE) + int l_read = netlink_recv(p_socket, l_buffer, l_size); + *p_size = l_read; + if (l_read == -2) { - *p_done = 1; - break; + free(l_buffer); + return NULL; } - - if (l_hdr->nlmsg_type == NLMSG_ERROR) + if (l_read >= 0) { - free(l_buffer); - return NULL; + pid_t l_pid = getpid(); + struct nlmsghdr* l_hdr; + for (l_hdr = (struct nlmsghdr*)l_buffer; NLMSG_OK(l_hdr, (unsigned int)l_read); + l_hdr = (struct nlmsghdr*)NLMSG_NEXT(l_hdr, l_read)) + { + if ((pid_t)l_hdr->nlmsg_pid != l_pid || (int)l_hdr->nlmsg_seq != p_socket) + { + continue; + } + + if (l_hdr->nlmsg_type == NLMSG_DONE) + { + *p_done = 1; + break; + } + + if (l_hdr->nlmsg_type == NLMSG_ERROR) + { + free(l_buffer); + return NULL; + } + } + return l_buffer; } - } - return l_buffer; - } - l_size *= 2; - } + l_size *= 2; + } } -static NetlinkList* -newListItem(struct nlmsghdr* p_data, unsigned int p_size) +static NetlinkList* newListItem(struct nlmsghdr* p_data, unsigned int p_size) { - NetlinkList* l_item = malloc(sizeof(NetlinkList)); - if (l_item == NULL) - { - return NULL; - } + NetlinkList* l_item = malloc(sizeof(NetlinkList)); + if (l_item == NULL) + { + return NULL; + } - l_item->m_next = NULL; - l_item->m_data = p_data; - l_item->m_size = p_size; - return l_item; + l_item->m_next = NULL; + l_item->m_data = p_data; + l_item->m_size = p_size; + return l_item; } -static void -freeResultList(NetlinkList* p_list) +static void freeResultList(NetlinkList* p_list) { - NetlinkList* l_cur; - while (p_list) - { - l_cur = p_list; - p_list = p_list->m_next; - free(l_cur->m_data); - free(l_cur); - } + NetlinkList* l_cur; + while (p_list) + { + l_cur = p_list; + p_list = p_list->m_next; + free(l_cur->m_data); + free(l_cur); + } } -static NetlinkList* -getResultList(int p_socket, int p_request) +static NetlinkList* getResultList(int p_socket, int p_request) { - if (netlink_send(p_socket, p_request) < 0) - { - return NULL; - } - - NetlinkList* l_list = NULL; - NetlinkList* l_end = NULL; - int l_size; - int l_done = 0; - while (!l_done) - { - struct nlmsghdr* l_hdr = getNetlinkResponse(p_socket, &l_size, &l_done); - if (!l_hdr) - { // error - freeResultList(l_list); - return NULL; - } - - NetlinkList* l_item = newListItem(l_hdr, l_size); - if (!l_item) - { - freeResultList(l_list); - return NULL; - } - if (!l_list) + if (netlink_send(p_socket, p_request) < 0) { - l_list = l_item; + return NULL; } - else + + NetlinkList* l_list = NULL; + NetlinkList* l_end = NULL; + int l_size; + int l_done = 0; + while (!l_done) { - l_end->m_next = l_item; + struct nlmsghdr* l_hdr = getNetlinkResponse(p_socket, &l_size, &l_done); + if (!l_hdr) + { // error + freeResultList(l_list); + return NULL; + } + + NetlinkList* l_item = newListItem(l_hdr, l_size); + if (!l_item) + { + freeResultList(l_list); + return NULL; + } + if (!l_list) + { + l_list = l_item; + } + else + { + l_end->m_next = l_item; + } + l_end = l_item; } - l_end = l_item; - } - return l_list; + return l_list; } -static size_t -maxSize(size_t a, size_t b) +static size_t maxSize(size_t a, size_t b) { - return (a > b ? a : b); + return (a > b ? a : b); } -static size_t -calcAddrLen(sa_family_t p_family, int p_dataSize) +static size_t calcAddrLen(sa_family_t p_family, int p_dataSize) { - switch (p_family) - { - case AF_INET: - return sizeof(struct sockaddr_in); - case AF_INET6: - return sizeof(struct sockaddr_in6); - case AF_PACKET: - return maxSize( - sizeof(struct sockaddr_ll), offsetof(struct sockaddr_ll, sll_addr) + p_dataSize); - default: - return maxSize(sizeof(struct sockaddr), offsetof(struct sockaddr, sa_data) + p_dataSize); - } + switch (p_family) + { + case AF_INET: + return sizeof(struct sockaddr_in); + case AF_INET6: + return sizeof(struct sockaddr_in6); + case AF_PACKET: + return maxSize( + sizeof(struct sockaddr_ll), offsetof(struct sockaddr_ll, sll_addr) + p_dataSize); + default: + return maxSize( + sizeof(struct sockaddr), offsetof(struct sockaddr, sa_data) + p_dataSize); + } } -static void -makeSockaddr(sa_family_t p_family, struct sockaddr* p_dest, void* p_data, size_t p_size) +static void makeSockaddr(sa_family_t p_family, struct sockaddr* p_dest, void* p_data, size_t p_size) { - switch (p_family) - { - case AF_INET: - memcpy(&((struct sockaddr_in*)p_dest)->sin_addr, p_data, p_size); - break; - case AF_INET6: - memcpy(&((struct sockaddr_in6*)p_dest)->sin6_addr, p_data, p_size); - break; - case AF_PACKET: - memcpy(((struct sockaddr_ll*)p_dest)->sll_addr, p_data, p_size); - ((struct sockaddr_ll*)p_dest)->sll_halen = p_size; - break; - default: - memcpy(p_dest->sa_data, p_data, p_size); - break; - } - p_dest->sa_family = p_family; + switch (p_family) + { + case AF_INET: + memcpy(&((struct sockaddr_in*)p_dest)->sin_addr, p_data, p_size); + break; + case AF_INET6: + memcpy(&((struct sockaddr_in6*)p_dest)->sin6_addr, p_data, p_size); + break; + case AF_PACKET: + memcpy(((struct sockaddr_ll*)p_dest)->sll_addr, p_data, p_size); + ((struct sockaddr_ll*)p_dest)->sll_halen = p_size; + break; + default: + memcpy(p_dest->sa_data, p_data, p_size); + break; + } + p_dest->sa_family = p_family; } -static void -addToEnd(struct ifaddrs** p_resultList, struct ifaddrs* p_entry) +static void addToEnd(struct ifaddrs** p_resultList, struct ifaddrs* p_entry) { - if (!*p_resultList) - { - *p_resultList = p_entry; - } - else - { - struct ifaddrs* l_cur = *p_resultList; - while (l_cur->ifa_next) + if (!*p_resultList) { - l_cur = l_cur->ifa_next; + *p_resultList = p_entry; + } + else + { + struct ifaddrs* l_cur = *p_resultList; + while (l_cur->ifa_next) + { + l_cur = l_cur->ifa_next; + } + l_cur->ifa_next = p_entry; } - l_cur->ifa_next = p_entry; - } } -static int -interpretLink(struct nlmsghdr* p_hdr, struct ifaddrs** p_resultList) +static int interpretLink(struct nlmsghdr* p_hdr, struct ifaddrs** p_resultList) { - struct ifinfomsg* l_info = (struct ifinfomsg*)NLMSG_DATA(p_hdr); - - size_t l_nameSize = 0; - size_t l_addrSize = 0; - size_t l_dataSize = 0; - - size_t l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifinfomsg)); - struct rtattr* l_rta; - for (l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize)) - { - void* l_rtaData = RTA_DATA(l_rta); - (void)l_rtaData; - size_t l_rtaDataSize = RTA_PAYLOAD(l_rta); - switch (l_rta->rta_type) - { - case IFLA_ADDRESS: - case IFLA_BROADCAST: - l_addrSize += NLMSG_ALIGN(calcAddrLen(AF_PACKET, l_rtaDataSize)); - break; - case IFLA_IFNAME: - l_nameSize += NLMSG_ALIGN(l_rtaSize + 1); - break; - case IFLA_STATS: - l_dataSize += NLMSG_ALIGN(l_rtaSize); - break; - default: - break; - } - } - - struct ifaddrs* l_entry = - malloc(sizeof(struct ifaddrs) + sizeof(int) + l_nameSize + l_addrSize + l_dataSize); - if (l_entry == NULL) - { - return -1; - } - memset(l_entry, 0, sizeof(struct ifaddrs)); - l_entry->ifa_name = ""; - - char* l_index = ((char*)l_entry) + sizeof(struct ifaddrs); - char* l_name = l_index + sizeof(int); - char* l_addr = l_name + l_nameSize; - char* l_data = l_addr + l_addrSize; - - // save the interface index so we can look it up when handling the addresses. - memcpy(l_index, &l_info->ifi_index, sizeof(int)); - - l_entry->ifa_flags = l_info->ifi_flags; - - l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifinfomsg)); - for (l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize)) - { - void* l_rtaData = RTA_DATA(l_rta); - size_t l_rtaDataSize = RTA_PAYLOAD(l_rta); - switch (l_rta->rta_type) + struct ifinfomsg* l_info = (struct ifinfomsg*)NLMSG_DATA(p_hdr); + + size_t l_nameSize = 0; + size_t l_addrSize = 0; + size_t l_dataSize = 0; + + size_t l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifinfomsg)); + struct rtattr* l_rta; + for (l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize)) { - case IFLA_ADDRESS: - case IFLA_BROADCAST: - { - size_t l_addrLen = calcAddrLen(AF_PACKET, l_rtaDataSize); - makeSockaddr(AF_PACKET, (struct sockaddr*)l_addr, l_rtaData, l_rtaDataSize); - ((struct sockaddr_ll*)l_addr)->sll_ifindex = l_info->ifi_index; - ((struct sockaddr_ll*)l_addr)->sll_hatype = l_info->ifi_type; - if (l_rta->rta_type == IFLA_ADDRESS) + void* l_rtaData = RTA_DATA(l_rta); + (void)l_rtaData; + size_t l_rtaDataSize = RTA_PAYLOAD(l_rta); + switch (l_rta->rta_type) { - l_entry->ifa_addr = (struct sockaddr*)l_addr; + case IFLA_ADDRESS: + case IFLA_BROADCAST: + l_addrSize += NLMSG_ALIGN(calcAddrLen(AF_PACKET, l_rtaDataSize)); + break; + case IFLA_IFNAME: + l_nameSize += NLMSG_ALIGN(l_rtaSize + 1); + break; + case IFLA_STATS: + l_dataSize += NLMSG_ALIGN(l_rtaSize); + break; + default: + break; } - else + } + + struct ifaddrs* l_entry = + malloc(sizeof(struct ifaddrs) + sizeof(int) + l_nameSize + l_addrSize + l_dataSize); + if (l_entry == NULL) + { + return -1; + } + memset(l_entry, 0, sizeof(struct ifaddrs)); + l_entry->ifa_name = ""; + + char* l_index = ((char*)l_entry) + sizeof(struct ifaddrs); + char* l_name = l_index + sizeof(int); + char* l_addr = l_name + l_nameSize; + char* l_data = l_addr + l_addrSize; + + // save the interface index so we can look it up when handling the addresses. + memcpy(l_index, &l_info->ifi_index, sizeof(int)); + + l_entry->ifa_flags = l_info->ifi_flags; + + l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifinfomsg)); + for (l_rta = IFLA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize)) + { + void* l_rtaData = RTA_DATA(l_rta); + size_t l_rtaDataSize = RTA_PAYLOAD(l_rta); + switch (l_rta->rta_type) { - l_entry->ifa_broadaddr = (struct sockaddr*)l_addr; + case IFLA_ADDRESS: + case IFLA_BROADCAST: + { + size_t l_addrLen = calcAddrLen(AF_PACKET, l_rtaDataSize); + makeSockaddr(AF_PACKET, (struct sockaddr*)l_addr, l_rtaData, l_rtaDataSize); + ((struct sockaddr_ll*)l_addr)->sll_ifindex = l_info->ifi_index; + ((struct sockaddr_ll*)l_addr)->sll_hatype = l_info->ifi_type; + if (l_rta->rta_type == IFLA_ADDRESS) + { + l_entry->ifa_addr = (struct sockaddr*)l_addr; + } + else + { + l_entry->ifa_broadaddr = (struct sockaddr*)l_addr; + } + l_addr += NLMSG_ALIGN(l_addrLen); + break; + } + case IFLA_IFNAME: + strncpy(l_name, l_rtaData, l_rtaDataSize); + l_name[l_rtaDataSize] = '\0'; + l_entry->ifa_name = l_name; + break; + case IFLA_STATS: + memcpy(l_data, l_rtaData, l_rtaDataSize); + l_entry->ifa_data = l_data; + break; + default: + break; } - l_addr += NLMSG_ALIGN(l_addrLen); - break; - } - case IFLA_IFNAME: - strncpy(l_name, l_rtaData, l_rtaDataSize); - l_name[l_rtaDataSize] = '\0'; - l_entry->ifa_name = l_name; - break; - case IFLA_STATS: - memcpy(l_data, l_rtaData, l_rtaDataSize); - l_entry->ifa_data = l_data; - break; - default: - break; } - } - addToEnd(p_resultList, l_entry); - return 0; + addToEnd(p_resultList, l_entry); + return 0; } -static struct ifaddrs* -findInterface(int p_index, struct ifaddrs** p_links, int p_numLinks) +static struct ifaddrs* findInterface(int p_index, struct ifaddrs** p_links, int p_numLinks) { - int l_num = 0; - struct ifaddrs* l_cur = *p_links; - while (l_cur && l_num < p_numLinks) - { - char* l_indexPtr = ((char*)l_cur) + sizeof(struct ifaddrs); - int l_index; - memcpy(&l_index, l_indexPtr, sizeof(int)); - if (l_index == p_index) + int l_num = 0; + struct ifaddrs* l_cur = *p_links; + while (l_cur && l_num < p_numLinks) { - return l_cur; - } + char* l_indexPtr = ((char*)l_cur) + sizeof(struct ifaddrs); + int l_index; + memcpy(&l_index, l_indexPtr, sizeof(int)); + if (l_index == p_index) + { + return l_cur; + } - l_cur = l_cur->ifa_next; - ++l_num; - } - return NULL; + l_cur = l_cur->ifa_next; + ++l_num; + } + return NULL; } -static int -interpretAddr(struct nlmsghdr* p_hdr, struct ifaddrs** p_resultList, int p_numLinks) +static int interpretAddr(struct nlmsghdr* p_hdr, struct ifaddrs** p_resultList, int p_numLinks) { - struct ifaddrmsg* l_info = (struct ifaddrmsg*)NLMSG_DATA(p_hdr); - struct ifaddrs* l_interface = findInterface(l_info->ifa_index, p_resultList, p_numLinks); + struct ifaddrmsg* l_info = (struct ifaddrmsg*)NLMSG_DATA(p_hdr); + struct ifaddrs* l_interface = findInterface(l_info->ifa_index, p_resultList, p_numLinks); - if (l_info->ifa_family == AF_PACKET) - { - return 0; - } - - size_t l_nameSize = 0; - size_t l_addrSize = 0; + if (l_info->ifa_family == AF_PACKET) + { + return 0; + } - int l_addedNetmask = 0; + size_t l_nameSize = 0; + size_t l_addrSize = 0; - size_t l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifaddrmsg)); - struct rtattr* l_rta; - for (l_rta = IFA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize)) - { - void* l_rtaData = RTA_DATA(l_rta); - (void)l_rtaData; - size_t l_rtaDataSize = RTA_PAYLOAD(l_rta); + int l_addedNetmask = 0; - switch (l_rta->rta_type) + size_t l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifaddrmsg)); + struct rtattr* l_rta; + for (l_rta = IFA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize)) { - case IFA_ADDRESS: - case IFA_LOCAL: - if ((l_info->ifa_family == AF_INET || l_info->ifa_family == AF_INET6) && !l_addedNetmask) - { // make room for netmask - l_addrSize += NLMSG_ALIGN(calcAddrLen(l_info->ifa_family, l_rtaDataSize)); - l_addedNetmask = 1; + void* l_rtaData = RTA_DATA(l_rta); + (void)l_rtaData; + size_t l_rtaDataSize = RTA_PAYLOAD(l_rta); + + switch (l_rta->rta_type) + { + case IFA_ADDRESS: + case IFA_LOCAL: + if ((l_info->ifa_family == AF_INET || l_info->ifa_family == AF_INET6) + && !l_addedNetmask) + { // make room for netmask + l_addrSize += NLMSG_ALIGN(calcAddrLen(l_info->ifa_family, l_rtaDataSize)); + l_addedNetmask = 1; + } + case IFA_BROADCAST: + l_addrSize += NLMSG_ALIGN(calcAddrLen(l_info->ifa_family, l_rtaDataSize)); + break; + case IFA_LABEL: + l_nameSize += NLMSG_ALIGN(l_rtaSize + 1); + break; + default: + break; } - case IFA_BROADCAST: - l_addrSize += NLMSG_ALIGN(calcAddrLen(l_info->ifa_family, l_rtaDataSize)); - break; - case IFA_LABEL: - l_nameSize += NLMSG_ALIGN(l_rtaSize + 1); - break; - default: - break; } - } - - struct ifaddrs* l_entry = malloc(sizeof(struct ifaddrs) + l_nameSize + l_addrSize); - if (l_entry == NULL) - { - return -1; - } - memset(l_entry, 0, sizeof(struct ifaddrs)); - l_entry->ifa_name = (l_interface ? l_interface->ifa_name : ""); - - char* l_name = ((char*)l_entry) + sizeof(struct ifaddrs); - char* l_addr = l_name + l_nameSize; - - l_entry->ifa_flags = l_info->ifa_flags; - if (l_interface) - { - l_entry->ifa_flags |= l_interface->ifa_flags; - } - - l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifaddrmsg)); - for (l_rta = IFA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize)) - { - void* l_rtaData = RTA_DATA(l_rta); - size_t l_rtaDataSize = RTA_PAYLOAD(l_rta); - switch (l_rta->rta_type) + + struct ifaddrs* l_entry = malloc(sizeof(struct ifaddrs) + l_nameSize + l_addrSize); + if (l_entry == NULL) + { + return -1; + } + memset(l_entry, 0, sizeof(struct ifaddrs)); + l_entry->ifa_name = (l_interface ? l_interface->ifa_name : ""); + + char* l_name = ((char*)l_entry) + sizeof(struct ifaddrs); + char* l_addr = l_name + l_nameSize; + + l_entry->ifa_flags = l_info->ifa_flags; + if (l_interface) + { + l_entry->ifa_flags |= l_interface->ifa_flags; + } + + l_rtaSize = NLMSG_PAYLOAD(p_hdr, sizeof(struct ifaddrmsg)); + for (l_rta = IFA_RTA(l_info); RTA_OK(l_rta, l_rtaSize); l_rta = RTA_NEXT(l_rta, l_rtaSize)) { - case IFA_ADDRESS: - case IFA_BROADCAST: - case IFA_LOCAL: - { - size_t l_addrLen = calcAddrLen(l_info->ifa_family, l_rtaDataSize); - makeSockaddr(l_info->ifa_family, (struct sockaddr*)l_addr, l_rtaData, l_rtaDataSize); - if (l_info->ifa_family == AF_INET6) + void* l_rtaData = RTA_DATA(l_rta); + size_t l_rtaDataSize = RTA_PAYLOAD(l_rta); + switch (l_rta->rta_type) { - if (IN6_IS_ADDR_LINKLOCAL((struct in6_addr*)l_rtaData) - || IN6_IS_ADDR_MC_LINKLOCAL((struct in6_addr*)l_rtaData)) - { - ((struct sockaddr_in6*)l_addr)->sin6_scope_id = l_info->ifa_index; - } + case IFA_ADDRESS: + case IFA_BROADCAST: + case IFA_LOCAL: + { + size_t l_addrLen = calcAddrLen(l_info->ifa_family, l_rtaDataSize); + makeSockaddr( + l_info->ifa_family, (struct sockaddr*)l_addr, l_rtaData, l_rtaDataSize); + if (l_info->ifa_family == AF_INET6) + { + if (IN6_IS_ADDR_LINKLOCAL((struct in6_addr*)l_rtaData) + || IN6_IS_ADDR_MC_LINKLOCAL((struct in6_addr*)l_rtaData)) + { + ((struct sockaddr_in6*)l_addr)->sin6_scope_id = l_info->ifa_index; + } + } + + if (l_rta->rta_type == IFA_ADDRESS) + { // apparently in a point-to-point network IFA_ADDRESS contains the + // dest address and IFA_LOCAL contains the local address + if (l_entry->ifa_addr) + { + l_entry->ifa_dstaddr = (struct sockaddr*)l_addr; + } + else + { + l_entry->ifa_addr = (struct sockaddr*)l_addr; + } + } + else if (l_rta->rta_type == IFA_LOCAL) + { + if (l_entry->ifa_addr) + { + l_entry->ifa_dstaddr = l_entry->ifa_addr; + } + l_entry->ifa_addr = (struct sockaddr*)l_addr; + } + else + { + l_entry->ifa_broadaddr = (struct sockaddr*)l_addr; + } + l_addr += NLMSG_ALIGN(l_addrLen); + break; + } + case IFA_LABEL: + strncpy(l_name, l_rtaData, l_rtaDataSize); + l_name[l_rtaDataSize] = '\0'; + l_entry->ifa_name = l_name; + break; + default: + break; } + } - if (l_rta->rta_type == IFA_ADDRESS) - { // apparently in a point-to-point network IFA_ADDRESS contains the - // dest address and IFA_LOCAL contains the local address - if (l_entry->ifa_addr) - { - l_entry->ifa_dstaddr = (struct sockaddr*)l_addr; - } - else - { - l_entry->ifa_addr = (struct sockaddr*)l_addr; - } - } - else if (l_rta->rta_type == IFA_LOCAL) + if (l_entry->ifa_addr + && (l_entry->ifa_addr->sa_family == AF_INET || l_entry->ifa_addr->sa_family == AF_INET6)) + { + unsigned l_maxPrefix = (l_entry->ifa_addr->sa_family == AF_INET ? 32 : 128); + unsigned l_prefix = + (l_info->ifa_prefixlen > l_maxPrefix ? l_maxPrefix : l_info->ifa_prefixlen); + char l_mask[16] = {0}; + unsigned i; + for (i = 0; i < (l_prefix / 8); ++i) { - if (l_entry->ifa_addr) - { - l_entry->ifa_dstaddr = l_entry->ifa_addr; - } - l_entry->ifa_addr = (struct sockaddr*)l_addr; + l_mask[i] = 0xff; } - else + if (l_prefix % 8) { - l_entry->ifa_broadaddr = (struct sockaddr*)l_addr; + l_mask[i] = 0xff << (8 - (l_prefix % 8)); } - l_addr += NLMSG_ALIGN(l_addrLen); - break; - } - case IFA_LABEL: - strncpy(l_name, l_rtaData, l_rtaDataSize); - l_name[l_rtaDataSize] = '\0'; - l_entry->ifa_name = l_name; - break; - default: - break; - } - } - - if (l_entry->ifa_addr - && (l_entry->ifa_addr->sa_family == AF_INET || l_entry->ifa_addr->sa_family == AF_INET6)) - { - unsigned l_maxPrefix = (l_entry->ifa_addr->sa_family == AF_INET ? 32 : 128); - unsigned l_prefix = (l_info->ifa_prefixlen > l_maxPrefix ? l_maxPrefix : l_info->ifa_prefixlen); - char l_mask[16] = {0}; - unsigned i; - for (i = 0; i < (l_prefix / 8); ++i) - { - l_mask[i] = 0xff; - } - if (l_prefix % 8) - { - l_mask[i] = 0xff << (8 - (l_prefix % 8)); - } - makeSockaddr(l_entry->ifa_addr->sa_family, (struct sockaddr*)l_addr, l_mask, l_maxPrefix / 8); - l_entry->ifa_netmask = (struct sockaddr*)l_addr; - } + makeSockaddr( + l_entry->ifa_addr->sa_family, (struct sockaddr*)l_addr, l_mask, l_maxPrefix / 8); + l_entry->ifa_netmask = (struct sockaddr*)l_addr; + } - addToEnd(p_resultList, l_entry); - return 0; + addToEnd(p_resultList, l_entry); + return 0; } -static int -interpretLinks(int p_socket, NetlinkList* p_netlinkList, struct ifaddrs** p_resultList) +static int interpretLinks(int p_socket, NetlinkList* p_netlinkList, struct ifaddrs** p_resultList) { - int l_numLinks = 0; - pid_t l_pid = getpid(); - for (; p_netlinkList; p_netlinkList = p_netlinkList->m_next) - { - unsigned int l_nlsize = p_netlinkList->m_size; - struct nlmsghdr* l_hdr; - for (l_hdr = p_netlinkList->m_data; NLMSG_OK(l_hdr, l_nlsize); - l_hdr = NLMSG_NEXT(l_hdr, l_nlsize)) + int l_numLinks = 0; + pid_t l_pid = getpid(); + for (; p_netlinkList; p_netlinkList = p_netlinkList->m_next) { - if ((pid_t)l_hdr->nlmsg_pid != l_pid || (int)l_hdr->nlmsg_seq != p_socket) - { - continue; - } - - if (l_hdr->nlmsg_type == NLMSG_DONE) - { - break; - } - - if (l_hdr->nlmsg_type == RTM_NEWLINK) - { - if (interpretLink(l_hdr, p_resultList) == -1) + unsigned int l_nlsize = p_netlinkList->m_size; + struct nlmsghdr* l_hdr; + for (l_hdr = p_netlinkList->m_data; NLMSG_OK(l_hdr, l_nlsize); + l_hdr = NLMSG_NEXT(l_hdr, l_nlsize)) { - return -1; + if ((pid_t)l_hdr->nlmsg_pid != l_pid || (int)l_hdr->nlmsg_seq != p_socket) + { + continue; + } + + if (l_hdr->nlmsg_type == NLMSG_DONE) + { + break; + } + + if (l_hdr->nlmsg_type == RTM_NEWLINK) + { + if (interpretLink(l_hdr, p_resultList) == -1) + { + return -1; + } + ++l_numLinks; + } } - ++l_numLinks; - } } - } - return l_numLinks; + return l_numLinks; } -static int -interpretAddrs( +static int interpretAddrs( int p_socket, NetlinkList* p_netlinkList, struct ifaddrs** p_resultList, int p_numLinks) { - pid_t l_pid = getpid(); - for (; p_netlinkList; p_netlinkList = p_netlinkList->m_next) - { - unsigned int l_nlsize = p_netlinkList->m_size; - struct nlmsghdr* l_hdr; - for (l_hdr = p_netlinkList->m_data; NLMSG_OK(l_hdr, l_nlsize); - l_hdr = NLMSG_NEXT(l_hdr, l_nlsize)) + pid_t l_pid = getpid(); + for (; p_netlinkList; p_netlinkList = p_netlinkList->m_next) { - if ((pid_t)l_hdr->nlmsg_pid != l_pid || (int)l_hdr->nlmsg_seq != p_socket) - { - continue; - } - - if (l_hdr->nlmsg_type == NLMSG_DONE) - { - break; - } - - if (l_hdr->nlmsg_type == RTM_NEWADDR) - { - if (interpretAddr(l_hdr, p_resultList, p_numLinks) == -1) + unsigned int l_nlsize = p_netlinkList->m_size; + struct nlmsghdr* l_hdr; + for (l_hdr = p_netlinkList->m_data; NLMSG_OK(l_hdr, l_nlsize); + l_hdr = NLMSG_NEXT(l_hdr, l_nlsize)) { - return -1; + if ((pid_t)l_hdr->nlmsg_pid != l_pid || (int)l_hdr->nlmsg_seq != p_socket) + { + continue; + } + + if (l_hdr->nlmsg_type == NLMSG_DONE) + { + break; + } + + if (l_hdr->nlmsg_type == RTM_NEWADDR) + { + if (interpretAddr(l_hdr, p_resultList, p_numLinks) == -1) + { + return -1; + } + } } - } } - } - return 0; + return 0; } -int -getifaddrs(struct ifaddrs** ifap) +int getifaddrs(struct ifaddrs** ifap) { - if (!ifap) - { - return -1; - } - *ifap = NULL; - - int l_socket = netlink_socket(); - if (l_socket < 0) - { - return -1; - } - - NetlinkList* l_linkResults = getResultList(l_socket, RTM_GETLINK); - if (!l_linkResults) - { - close(l_socket); - return -1; - } + if (!ifap) + { + return -1; + } + *ifap = NULL; + + int l_socket = netlink_socket(); + if (l_socket < 0) + { + return -1; + } + + NetlinkList* l_linkResults = getResultList(l_socket, RTM_GETLINK); + if (!l_linkResults) + { + close(l_socket); + return -1; + } + + NetlinkList* l_addrResults = getResultList(l_socket, RTM_GETADDR); + if (!l_addrResults) + { + close(l_socket); + freeResultList(l_linkResults); + return -1; + } + + int l_result = 0; + int l_numLinks = interpretLinks(l_socket, l_linkResults, ifap); + if (l_numLinks == -1 || interpretAddrs(l_socket, l_addrResults, ifap, l_numLinks) == -1) + { + l_result = -1; + } - NetlinkList* l_addrResults = getResultList(l_socket, RTM_GETADDR); - if (!l_addrResults) - { - close(l_socket); freeResultList(l_linkResults); - return -1; - } - - int l_result = 0; - int l_numLinks = interpretLinks(l_socket, l_linkResults, ifap); - if (l_numLinks == -1 || interpretAddrs(l_socket, l_addrResults, ifap, l_numLinks) == -1) - { - l_result = -1; - } - - freeResultList(l_linkResults); - freeResultList(l_addrResults); - close(l_socket); - return l_result; + freeResultList(l_addrResults); + close(l_socket); + return l_result; } -void -freeifaddrs(struct ifaddrs* ifa) +void freeifaddrs(struct ifaddrs* ifa) { - struct ifaddrs* l_cur; - while (ifa) - { - l_cur = ifa; - ifa = ifa->ifa_next; - free(l_cur); - } + struct ifaddrs* l_cur; + while (ifa) + { + l_cur = ifa; + ifa = ifa->ifa_next; + free(l_cur); + } } diff --git a/llarp/android/ifaddrs.h b/llarp/android/ifaddrs.h index 03a9fbe93..89ed7b425 100644 --- a/llarp/android/ifaddrs.h +++ b/llarp/android/ifaddrs.h @@ -27,13 +27,13 @@ struct ifaddrs { - struct ifaddrs* ifa_next; - char* ifa_name; - unsigned int ifa_flags; - struct sockaddr* ifa_addr; - struct sockaddr* ifa_netmask; - struct sockaddr* ifa_dstaddr; - void* ifa_data; + struct ifaddrs* ifa_next; + char* ifa_name; + unsigned int ifa_flags; + struct sockaddr* ifa_addr; + struct sockaddr* ifa_netmask; + struct sockaddr* ifa_dstaddr; + void* ifa_data; }; /* @@ -47,8 +47,6 @@ struct ifaddrs #include __BEGIN_DECLS -extern int -getifaddrs(struct ifaddrs** ifap); -extern void -freeifaddrs(struct ifaddrs* ifa); +extern int getifaddrs(struct ifaddrs** ifap); +extern void freeifaddrs(struct ifaddrs* ifa); __END_DECLS diff --git a/llarp/apple/DNSTrampoline.h b/llarp/apple/DNSTrampoline.h index e691caf75..7281ae51d 100644 --- a/llarp/apple/DNSTrampoline.h +++ b/llarp/apple/DNSTrampoline.h @@ -22,23 +22,24 @@ extern NSString* error_domain; */ @interface LLARPDNSTrampoline : NSObject { - // The socket libunbound talks with: - uv_udp_t request_socket; - // The reply address. This is a bit hacky: we configure libunbound to just use single address - // (rather than a range) so that we don't have to worry about tracking different reply addresses. - @public - struct sockaddr reply_addr; - // UDP "session" aimed at the upstream DNS - @public - NWUDPSession* upstream; - // Apple docs say writes could take time *and* the crappy Apple datagram write methods aren't - // callable again until the previous write finishes. Deal with this garbage API by queuing - // everything than using a uv_async to process the queue. - @public - int write_ready; - @public - NSMutableArray* pending_writes; - uv_async_t write_trigger; + // The socket libunbound talks with: + uv_udp_t request_socket; + // The reply address. This is a bit hacky: we configure libunbound to just use single address + // (rather than a range) so that we don't have to worry about tracking different reply + // addresses. + @public + struct sockaddr reply_addr; + // UDP "session" aimed at the upstream DNS + @public + NWUDPSession* upstream; + // Apple docs say writes could take time *and* the crappy Apple datagram write methods aren't + // callable again until the previous write finishes. Deal with this garbage API by queuing + // everything than using a uv_async to process the queue. + @public + int write_ready; + @public + NSMutableArray* pending_writes; + uv_async_t write_trigger; } - (void)startWithUpstreamDns:(NWUDPSession*)dns listenIp:(NSString*)listenIp diff --git a/llarp/apple/DNSTrampoline.m b/llarp/apple/DNSTrampoline.m index 8ccbb1d2e..237b72b44 100644 --- a/llarp/apple/DNSTrampoline.m +++ b/llarp/apple/DNSTrampoline.m @@ -6,80 +6,76 @@ NSString* error_domain = @"org.lokinet"; // Receiving an incoming packet, presumably from libunbound. NB: this is called from the libuv // event loop. -static void -on_request( +static void on_request( uv_udp_t* socket, ssize_t nread, const uv_buf_t* buf, const struct sockaddr* addr, unsigned flags) { - (void)flags; - if (nread < 0) - { - NSLog(@"Read error: %s", uv_strerror(nread)); - free(buf->base); - return; - } - - if (nread == 0 || !addr) - { - if (buf) - free(buf->base); - return; - } - - LLARPDNSTrampoline* t = (__bridge LLARPDNSTrampoline*)socket->data; - - // We configure libunbound to use just one single port so we'll just send replies to the last port - // to talk to us. (And we're only listening on localhost in the first place). - t->reply_addr = *addr; - - // NSData takes care of calling free(buf->base) for us with this constructor: - [t->pending_writes addObject:[NSData dataWithBytesNoCopy:buf->base length:nread]]; - - [t flushWrites]; + (void)flags; + if (nread < 0) + { + NSLog(@"Read error: %s", uv_strerror(nread)); + free(buf->base); + return; + } + + if (nread == 0 || !addr) + { + if (buf) + free(buf->base); + return; + } + + LLARPDNSTrampoline* t = (__bridge LLARPDNSTrampoline*)socket->data; + + // We configure libunbound to use just one single port so we'll just send replies to the last + // port to talk to us. (And we're only listening on localhost in the first place). + t->reply_addr = *addr; + + // NSData takes care of calling free(buf->base) for us with this constructor: + [t->pending_writes addObject:[NSData dataWithBytesNoCopy:buf->base length:nread]]; + + [t flushWrites]; } -static void -on_sent(uv_udp_send_t* req, int status) +static void on_sent(uv_udp_send_t* req, int status) { - (void)status; - NSArray* datagrams = (__bridge_transfer NSArray*)req->data; - (void)datagrams; - free(req); + (void)status; + NSArray* datagrams = (__bridge_transfer NSArray*)req->data; + (void)datagrams; + free(req); } // NB: called from the libuv event loop (so we don't have to worry about the above and this one // running at once from different threads). -static void -write_flusher(uv_async_t* async) +static void write_flusher(uv_async_t* async) { - LLARPDNSTrampoline* t = (__bridge LLARPDNSTrampoline*)async->data; - if (t->pending_writes.count == 0) - return; - - NSArray* data = [NSArray arrayWithArray:t->pending_writes]; - [t->pending_writes removeAllObjects]; - __weak LLARPDNSTrampoline* weakSelf = t; - [t->upstream writeMultipleDatagrams:data - completionHandler:^(NSError* error) { - if (error) - NSLog(@"Failed to send request to upstream DNS: %@", error); - - // Trigger another flush in case anything built up while Apple was doing its - // things. Just call it unconditionally (rather than checking the queue) - // because this handler is probably running in some other thread. - [weakSelf flushWrites]; - }]; + LLARPDNSTrampoline* t = (__bridge LLARPDNSTrampoline*)async->data; + if (t->pending_writes.count == 0) + return; + + NSArray* data = [NSArray arrayWithArray:t->pending_writes]; + [t->pending_writes removeAllObjects]; + __weak LLARPDNSTrampoline* weakSelf = t; + [t->upstream writeMultipleDatagrams:data + completionHandler:^(NSError* error) { + if (error) + NSLog(@"Failed to send request to upstream DNS: %@", error); + + // Trigger another flush in case anything built up while Apple was doing its + // things. Just call it unconditionally (rather than checking the queue) + // because this handler is probably running in some other thread. + [weakSelf flushWrites]; + }]; } -static void -alloc_buffer(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) +static void alloc_buffer(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) { - (void)handle; - buf->base = malloc(suggested_size); - buf->len = suggested_size; + (void)handle; + buf->base = malloc(suggested_size); + buf->len = suggested_size; } @implementation LLARPDNSTrampoline @@ -90,78 +86,78 @@ alloc_buffer(uv_handle_t* handle, size_t suggested_size, uv_buf_t* buf) uvLoop:(uv_loop_t*)loop completionHandler:(void (^)(NSError* error))completionHandler { - NSLog(@"Setting up trampoline"); - pending_writes = [[NSMutableArray alloc] init]; - write_trigger.data = (__bridge void*)self; - uv_async_init(loop, &write_trigger, write_flusher); - - request_socket.data = (__bridge void*)self; - uv_udp_init(loop, &request_socket); - struct sockaddr_in recv_addr; - uv_ip4_addr(listenIp.UTF8String, listenPort, &recv_addr); - int ret = uv_udp_bind(&request_socket, (const struct sockaddr*)&recv_addr, UV_UDP_REUSEADDR); - if (ret < 0) - { - NSString* errstr = - [NSString stringWithFormat:@"Failed to start DNS trampoline: %s", uv_strerror(ret)]; - NSError* err = [NSError errorWithDomain:error_domain code:ret userInfo:@{@"Error": errstr}]; - NSLog(@"%@", err); - return completionHandler(err); - } - uv_udp_recv_start(&request_socket, alloc_buffer, on_request); - - NSLog(@"Starting DNS trampoline"); - - upstream = dns; - __weak LLARPDNSTrampoline* weakSelf = self; - [upstream - setReadHandler:^(NSArray* datagrams, NSError* error) { - // Reading a reply back from the UDP socket used to talk to upstream - if (error) - { - NSLog(@"Reader handler failed: %@", error); - return; + NSLog(@"Setting up trampoline"); + pending_writes = [[NSMutableArray alloc] init]; + write_trigger.data = (__bridge void*)self; + uv_async_init(loop, &write_trigger, write_flusher); + + request_socket.data = (__bridge void*)self; + uv_udp_init(loop, &request_socket); + struct sockaddr_in recv_addr; + uv_ip4_addr(listenIp.UTF8String, listenPort, &recv_addr); + int ret = uv_udp_bind(&request_socket, (const struct sockaddr*)&recv_addr, UV_UDP_REUSEADDR); + if (ret < 0) + { + NSString* errstr = + [NSString stringWithFormat:@"Failed to start DNS trampoline: %s", uv_strerror(ret)]; + NSError* err = [NSError errorWithDomain:error_domain code:ret userInfo:@{@"Error": errstr}]; + NSLog(@"%@", err); + return completionHandler(err); + } + uv_udp_recv_start(&request_socket, alloc_buffer, on_request); + + NSLog(@"Starting DNS trampoline"); + + upstream = dns; + __weak LLARPDNSTrampoline* weakSelf = self; + [upstream + setReadHandler:^(NSArray* datagrams, NSError* error) { + // Reading a reply back from the UDP socket used to talk to upstream + if (error) + { + NSLog(@"Reader handler failed: %@", error); + return; + } + LLARPDNSTrampoline* strongSelf = weakSelf; + if (!strongSelf || datagrams.count == 0) + return; + + uv_buf_t* buffers = malloc(datagrams.count * sizeof(uv_buf_t)); + size_t buf_count = 0; + for (NSData* packet in datagrams) + { + buffers[buf_count].base = (void*)packet.bytes; + buffers[buf_count].len = packet.length; + buf_count++; + } + uv_udp_send_t* uvsend = malloc(sizeof(uv_udp_send_t)); + uvsend->data = (__bridge_retained void*)datagrams; + int ret = uv_udp_send( + uvsend, + &strongSelf->request_socket, + buffers, + buf_count, + &strongSelf->reply_addr, + on_sent); + free(buffers); + if (ret < 0) + NSLog(@"Error returning DNS responses to unbound: %s", uv_strerror(ret)); } - LLARPDNSTrampoline* strongSelf = weakSelf; - if (!strongSelf || datagrams.count == 0) - return; - - uv_buf_t* buffers = malloc(datagrams.count * sizeof(uv_buf_t)); - size_t buf_count = 0; - for (NSData* packet in datagrams) - { - buffers[buf_count].base = (void*)packet.bytes; - buffers[buf_count].len = packet.length; - buf_count++; - } - uv_udp_send_t* uvsend = malloc(sizeof(uv_udp_send_t)); - uvsend->data = (__bridge_retained void*)datagrams; - int ret = uv_udp_send( - uvsend, - &strongSelf->request_socket, - buffers, - buf_count, - &strongSelf->reply_addr, - on_sent); - free(buffers); - if (ret < 0) - NSLog(@"Error returning DNS responses to unbound: %s", uv_strerror(ret)); - } - maxDatagrams:NSUIntegerMax]; - - completionHandler(nil); + maxDatagrams:NSUIntegerMax]; + + completionHandler(nil); } - (void)flushWrites { - uv_async_send(&write_trigger); + uv_async_send(&write_trigger); } - (void)dealloc { - NSLog(@"Stopping DNS trampoline"); - uv_close((uv_handle_t*)&request_socket, NULL); - uv_close((uv_handle_t*)&write_trigger, NULL); + NSLog(@"Stopping DNS trampoline"); + uv_close((uv_handle_t*)&request_socket, NULL); + uv_close((uv_handle_t*)&write_trigger, NULL); } @end diff --git a/llarp/apple/PacketTunnelProvider.m b/llarp/apple/PacketTunnelProvider.m index 1981b0b81..5f848fd1e 100644 --- a/llarp/apple/PacketTunnelProvider.m +++ b/llarp/apple/PacketTunnelProvider.m @@ -8,15 +8,15 @@ @interface LLARPPacketTunnel : NEPacketTunnelProvider { - void* lokinet; - llarp_incoming_packet packet_buf[LLARP_APPLE_PACKET_BUF_SIZE]; - @public - NEPacketTunnelNetworkSettings* settings; - @public - NEIPv4Route* tun_route4; - @public - NEIPv6Route* tun_route6; - LLARPDNSTrampoline* dns_tramp; + void* lokinet; + llarp_incoming_packet packet_buf[LLARP_APPLE_PACKET_BUF_SIZE]; + @public + NEPacketTunnelNetworkSettings* settings; + @public + NEIPv4Route* tun_route4; + @public + NEIPv6Route* tun_route6; + LLARPDNSTrampoline* dns_tramp; } - (void)startTunnelWithOptions:(NSDictionary*)options @@ -34,368 +34,364 @@ @end -static void -nslogger(const char* msg) +static void nslogger(const char* msg) { - NSLog(@"%s", msg); + NSLog(@"%s", msg); } -static void -packet_writer(int af, const void* data, size_t size, void* ctx) +static void packet_writer(int af, const void* data, size_t size, void* ctx) { - if (ctx == nil || data == nil) - return; + if (ctx == nil || data == nil) + return; - NSData* buf = [NSData dataWithBytesNoCopy:(void*)data length:size freeWhenDone:NO]; - LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*)ctx; - [t.packetFlow writePackets:@[buf] withProtocols:@[[NSNumber numberWithInt:af]]]; + NSData* buf = [NSData dataWithBytesNoCopy:(void*)data length:size freeWhenDone:NO]; + LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*)ctx; + [t.packetFlow writePackets:@[buf] withProtocols:@[[NSNumber numberWithInt:af]]]; } -static void -start_packet_reader(void* ctx) +static void start_packet_reader(void* ctx) { - if (ctx == nil) - return; + if (ctx == nil) + return; - LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*)ctx; - [t readPackets]; + LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*)ctx; + [t readPackets]; } -static void -add_ipv4_route(const char* addr, const char* netmask, void* ctx) +static void add_ipv4_route(const char* addr, const char* netmask, void* ctx) { - NSLog(@"Adding IPv4 route %s:%s to packet tunnel", addr, netmask); - NEIPv4Route* route = - [[NEIPv4Route alloc] initWithDestinationAddress:[NSString stringWithUTF8String:addr] - subnetMask:[NSString stringWithUTF8String:netmask]]; + NSLog(@"Adding IPv4 route %s:%s to packet tunnel", addr, netmask); + NEIPv4Route* route = + [[NEIPv4Route alloc] initWithDestinationAddress:[NSString stringWithUTF8String:addr] + subnetMask:[NSString stringWithUTF8String:netmask]]; - LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*)ctx; - for (NEIPv4Route* r in t->settings.IPv4Settings.includedRoutes) - if ([r.destinationAddress isEqualToString:route.destinationAddress] && - [r.destinationSubnetMask isEqualToString:route.destinationSubnetMask]) - return; // Already in the settings, nothing to add. + LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*)ctx; + for (NEIPv4Route* r in t->settings.IPv4Settings.includedRoutes) + if ([r.destinationAddress isEqualToString:route.destinationAddress] && + [r.destinationSubnetMask isEqualToString:route.destinationSubnetMask]) + return; // Already in the settings, nothing to add. - t->settings.IPv4Settings.includedRoutes = - [t->settings.IPv4Settings.includedRoutes arrayByAddingObject:route]; + t->settings.IPv4Settings.includedRoutes = + [t->settings.IPv4Settings.includedRoutes arrayByAddingObject:route]; - [t updateNetworkSettings]; + [t updateNetworkSettings]; } -static void -del_ipv4_route(const char* addr, const char* netmask, void* ctx) +static void del_ipv4_route(const char* addr, const char* netmask, void* ctx) { - NSLog(@"Removing IPv4 route %s:%s to packet tunnel", addr, netmask); - NEIPv4Route* route = - [[NEIPv4Route alloc] initWithDestinationAddress:[NSString stringWithUTF8String:addr] - subnetMask:[NSString stringWithUTF8String:netmask]]; - - LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*)ctx; - NSMutableArray* routes = - [NSMutableArray arrayWithArray:t->settings.IPv4Settings.includedRoutes]; - for (size_t i = 0; i < routes.count; i++) - { - if ([routes[i].destinationAddress isEqualToString:route.destinationAddress] && - [routes[i].destinationSubnetMask isEqualToString:route.destinationSubnetMask]) + NSLog(@"Removing IPv4 route %s:%s to packet tunnel", addr, netmask); + NEIPv4Route* route = + [[NEIPv4Route alloc] initWithDestinationAddress:[NSString stringWithUTF8String:addr] + subnetMask:[NSString stringWithUTF8String:netmask]]; + + LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*)ctx; + NSMutableArray* routes = + [NSMutableArray arrayWithArray:t->settings.IPv4Settings.includedRoutes]; + for (size_t i = 0; i < routes.count; i++) { - [routes removeObjectAtIndex:i]; - i--; + if ([routes[i].destinationAddress isEqualToString:route.destinationAddress] && + [routes[i].destinationSubnetMask isEqualToString:route.destinationSubnetMask]) + { + [routes removeObjectAtIndex:i]; + i--; + } } - } - if (routes.count != t->settings.IPv4Settings.includedRoutes.count) - { - t->settings.IPv4Settings.includedRoutes = routes; - [t updateNetworkSettings]; - } + if (routes.count != t->settings.IPv4Settings.includedRoutes.count) + { + t->settings.IPv4Settings.includedRoutes = routes; + [t updateNetworkSettings]; + } } -static void -add_ipv6_route(const char* addr, int prefix, void* ctx) +static void add_ipv6_route(const char* addr, int prefix, void* ctx) { - NEIPv6Route* route = - [[NEIPv6Route alloc] initWithDestinationAddress:[NSString stringWithUTF8String:addr] - networkPrefixLength:[NSNumber numberWithInt:prefix]]; + NEIPv6Route* route = + [[NEIPv6Route alloc] initWithDestinationAddress:[NSString stringWithUTF8String:addr] + networkPrefixLength:[NSNumber numberWithInt:prefix]]; - LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*)ctx; - for (NEIPv6Route* r in t->settings.IPv6Settings.includedRoutes) - if ([r.destinationAddress isEqualToString:route.destinationAddress] && - [r.destinationNetworkPrefixLength isEqualToNumber:route.destinationNetworkPrefixLength]) - return; // Already in the settings, nothing to add. + LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*)ctx; + for (NEIPv6Route* r in t->settings.IPv6Settings.includedRoutes) + if ([r.destinationAddress isEqualToString:route.destinationAddress] && + [r.destinationNetworkPrefixLength isEqualToNumber:route.destinationNetworkPrefixLength]) + return; // Already in the settings, nothing to add. - t->settings.IPv6Settings.includedRoutes = - [t->settings.IPv6Settings.includedRoutes arrayByAddingObject:route]; + t->settings.IPv6Settings.includedRoutes = + [t->settings.IPv6Settings.includedRoutes arrayByAddingObject:route]; - [t updateNetworkSettings]; + [t updateNetworkSettings]; } -static void -del_ipv6_route(const char* addr, int prefix, void* ctx) +static void del_ipv6_route(const char* addr, int prefix, void* ctx) { - NEIPv6Route* route = - [[NEIPv6Route alloc] initWithDestinationAddress:[NSString stringWithUTF8String:addr] - networkPrefixLength:[NSNumber numberWithInt:prefix]]; - - LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*)ctx; - NSMutableArray* routes = - [NSMutableArray arrayWithArray:t->settings.IPv6Settings.includedRoutes]; - for (size_t i = 0; i < routes.count; i++) - { - if ([routes[i].destinationAddress isEqualToString:route.destinationAddress] && - [routes[i].destinationNetworkPrefixLength - isEqualToNumber:route.destinationNetworkPrefixLength]) + NEIPv6Route* route = + [[NEIPv6Route alloc] initWithDestinationAddress:[NSString stringWithUTF8String:addr] + networkPrefixLength:[NSNumber numberWithInt:prefix]]; + + LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*)ctx; + NSMutableArray* routes = + [NSMutableArray arrayWithArray:t->settings.IPv6Settings.includedRoutes]; + for (size_t i = 0; i < routes.count; i++) { - [routes removeObjectAtIndex:i]; - i--; + if ([routes[i].destinationAddress isEqualToString:route.destinationAddress] && + [routes[i].destinationNetworkPrefixLength + isEqualToNumber:route.destinationNetworkPrefixLength]) + { + [routes removeObjectAtIndex:i]; + i--; + } } - } - if (routes.count != t->settings.IPv6Settings.includedRoutes.count) - { - t->settings.IPv6Settings.includedRoutes = routes; - [t updateNetworkSettings]; - } + if (routes.count != t->settings.IPv6Settings.includedRoutes.count) + { + t->settings.IPv6Settings.includedRoutes = routes; + [t updateNetworkSettings]; + } } -static void -add_default_route(void* ctx) +static void add_default_route(void* ctx) { - NSLog(@"Making the tunnel the default route"); - LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*)ctx; + NSLog(@"Making the tunnel the default route"); + LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*)ctx; - t->settings.IPv4Settings.includedRoutes = @[NEIPv4Route.defaultRoute]; - t->settings.IPv6Settings.includedRoutes = @[NEIPv6Route.defaultRoute]; + t->settings.IPv4Settings.includedRoutes = @[NEIPv4Route.defaultRoute]; + t->settings.IPv6Settings.includedRoutes = @[NEIPv6Route.defaultRoute]; - [t updateNetworkSettings]; + [t updateNetworkSettings]; } -static void -del_default_route(void* ctx) +static void del_default_route(void* ctx) { - NSLog(@"Removing default route from tunnel"); - LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*)ctx; + NSLog(@"Removing default route from tunnel"); + LLARPPacketTunnel* t = (__bridge LLARPPacketTunnel*)ctx; - t->settings.IPv4Settings.includedRoutes = @[t->tun_route4]; - t->settings.IPv6Settings.includedRoutes = @[t->tun_route6]; + t->settings.IPv4Settings.includedRoutes = @[t->tun_route4]; + t->settings.IPv6Settings.includedRoutes = @[t->tun_route6]; - [t updateNetworkSettings]; + [t updateNetworkSettings]; } @implementation LLARPPacketTunnel - (void)readPackets { - [self.packetFlow readPacketObjectsWithCompletionHandler:^(NSArray* packets) { - if (lokinet == nil) - return; + [self.packetFlow readPacketObjectsWithCompletionHandler:^(NSArray* packets) { + if (lokinet == nil) + return; - size_t size = 0; - for (NEPacket* p in packets) - { - packet_buf[size].bytes = p.data.bytes; - packet_buf[size].size = p.data.length; - size++; - if (size >= LLARP_APPLE_PACKET_BUF_SIZE) + size_t size = 0; + for (NEPacket* p in packets) { - llarp_apple_incoming(lokinet, packet_buf, size); - size = 0; + packet_buf[size].bytes = p.data.bytes; + packet_buf[size].size = p.data.length; + size++; + if (size >= LLARP_APPLE_PACKET_BUF_SIZE) + { + llarp_apple_incoming(lokinet, packet_buf, size); + size = 0; + } } - } - if (size > 0) - llarp_apple_incoming(lokinet, packet_buf, size); + if (size > 0) + llarp_apple_incoming(lokinet, packet_buf, size); - [self readPackets]; - }]; + [self readPackets]; + }]; } - (void)startTunnelWithOptions:(NSDictionary*)options completionHandler:(void (^)(NSError*))completionHandler { - NSString* default_bootstrap = [NSBundle.mainBundle pathForResource:@"bootstrap" ofType:@"signed"]; - NSString* home = NSHomeDirectory(); - - llarp_apple_config conf = { - .config_dir = home.UTF8String, - .default_bootstrap = default_bootstrap.UTF8String, - .ns_logger = nslogger, - .packet_writer = packet_writer, - .start_reading = start_packet_reader, - .route_callbacks = - {.add_ipv4_route = add_ipv4_route, - .del_ipv4_route = del_ipv4_route, - .add_ipv6_route = add_ipv6_route, - .del_ipv6_route = del_ipv6_route, - .add_default_route = add_default_route, - .del_default_route = del_default_route}, - }; - - lokinet = llarp_apple_init(&conf); - if (!lokinet) - { - NSError* init_failure = [NSError errorWithDomain:error_domain - code:500 - userInfo:@{@"Error": @"Failed to initialize lokinet"}]; - NSLog(@"%@", [init_failure localizedDescription]); - return completionHandler(init_failure); - } - - NSString* ip = [NSString stringWithUTF8String:conf.tunnel_ipv4_ip]; - NSString* mask = [NSString stringWithUTF8String:conf.tunnel_ipv4_netmask]; - - // We don't have a fixed address so just stick some bogus value here: - settings = [[NEPacketTunnelNetworkSettings alloc] initWithTunnelRemoteAddress:@"127.3.2.1"]; + NSString* default_bootstrap = [NSBundle.mainBundle pathForResource:@"bootstrap" + ofType:@"signed"]; + NSString* home = NSHomeDirectory(); + + llarp_apple_config conf = { + .config_dir = home.UTF8String, + .default_bootstrap = default_bootstrap.UTF8String, + .ns_logger = nslogger, + .packet_writer = packet_writer, + .start_reading = start_packet_reader, + .route_callbacks = + {.add_ipv4_route = add_ipv4_route, + .del_ipv4_route = del_ipv4_route, + .add_ipv6_route = add_ipv6_route, + .del_ipv6_route = del_ipv6_route, + .add_default_route = add_default_route, + .del_default_route = del_default_route}, + }; + + lokinet = llarp_apple_init(&conf); + if (!lokinet) + { + NSError* init_failure = + [NSError errorWithDomain:error_domain + code:500 + userInfo:@{@"Error": @"Failed to initialize lokinet"}]; + NSLog(@"%@", [init_failure localizedDescription]); + return completionHandler(init_failure); + } + + NSString* ip = [NSString stringWithUTF8String:conf.tunnel_ipv4_ip]; + NSString* mask = [NSString stringWithUTF8String:conf.tunnel_ipv4_netmask]; + + // We don't have a fixed address so just stick some bogus value here: + settings = [[NEPacketTunnelNetworkSettings alloc] initWithTunnelRemoteAddress:@"127.3.2.1"]; #ifdef MACOS_SYSTEM_EXTENSION - NSString* dns_ip = [NSString stringWithUTF8String:conf.dns_bind_ip]; + NSString* dns_ip = [NSString stringWithUTF8String:conf.dns_bind_ip]; #else - // TODO: placeholder - NSString* dns_ip = ip; + // TODO: placeholder + NSString* dns_ip = ip; #endif - NSLog(@"setting dns to %@", dns_ip); - NEDNSSettings* dns = [[NEDNSSettings alloc] initWithServers:@[dns_ip]]; - dns.domainName = @"localhost.loki"; - dns.matchDomains = @[@""]; - // In theory, matchDomains is supposed to be set to DNS suffixes that we resolve. This seems - // highly unreliable, though: often it just doesn't work at all (perhaps only if we make ourselves - // the default route?), and even when it does work, it seems there are secret reasons that some - // domains (such as instagram.com) still won't work because there's some magic sauce in the OS - // that Apple engineers don't want to disclose ("This is what I expected, actually. Although I - // will not comment on what I believe is happening here", from - // https://developer.apple.com/forums/thread/685410). - // - // So the documentation sucks and the feature doesn't appear to work, so as much as it would be - // nice to capture only .loki and .snode when not in exit mode, we can't, so capture everything - // and use our default upstream. - dns.matchDomains = @[@""]; - dns.matchDomainsNoSearch = true; - dns.searchDomains = @[]; - settings.DNSSettings = dns; - - NWHostEndpoint* upstreamdns_ep; - if (strlen(conf.upstream_dns)) - upstreamdns_ep = - [NWHostEndpoint endpointWithHostname:[NSString stringWithUTF8String:conf.upstream_dns] - port:@(conf.upstream_dns_port).stringValue]; - - NEIPv4Settings* ipv4 = [[NEIPv4Settings alloc] initWithAddresses:@[ip] subnetMasks:@[mask]]; - tun_route4 = [[NEIPv4Route alloc] initWithDestinationAddress:ip subnetMask:mask]; - ipv4.includedRoutes = @[tun_route4]; - settings.IPv4Settings = ipv4; - - NSString* ip6 = [NSString stringWithUTF8String:conf.tunnel_ipv6_ip]; - NSNumber* ip6_prefix = [NSNumber numberWithUnsignedInt:conf.tunnel_ipv6_prefix]; - NEIPv6Settings* ipv6 = [[NEIPv6Settings alloc] initWithAddresses:@[ip6] - networkPrefixLengths:@[ip6_prefix]]; - tun_route6 = [[NEIPv6Route alloc] initWithDestinationAddress:ip6 networkPrefixLength:ip6_prefix]; - ipv6.includedRoutes = @[tun_route6]; - settings.IPv6Settings = ipv6; - - __weak LLARPPacketTunnel* weakSelf = self; - [self setTunnelNetworkSettings:settings - completionHandler:^(NSError* err) { - if (err) - { - NSLog(@"Failed to configure lokinet tunnel: %@", err); - return completionHandler(err); - } - LLARPPacketTunnel* strongSelf = weakSelf; - if (!strongSelf) - return completionHandler(nil); - - int start_ret = llarp_apple_start(strongSelf->lokinet, (__bridge void*)strongSelf); - if (start_ret != 0) - { - NSError* start_failure = - [NSError errorWithDomain:error_domain - code:start_ret - userInfo:@{@"Error": @"Failed to start lokinet"}]; - NSLog(@"%@", start_failure); - lokinet = nil; - return completionHandler(start_failure); - } - - NSString* dns_tramp_ip = @"127.0.0.1"; - NSLog( - @"Starting DNS exit mode trampoline to %@ on %@:%d", - upstreamdns_ep, - dns_tramp_ip, - dns_trampoline_port); - NWUDPSession* upstreamdns = - [strongSelf createUDPSessionThroughTunnelToEndpoint:upstreamdns_ep - fromEndpoint:nil]; - strongSelf->dns_tramp = [LLARPDNSTrampoline alloc]; - [strongSelf->dns_tramp - startWithUpstreamDns:upstreamdns - listenIp:dns_tramp_ip - listenPort:dns_trampoline_port - uvLoop:llarp_apple_get_uv_loop(strongSelf->lokinet) - completionHandler:^(NSError* error) { - if (error) - NSLog(@"Error starting dns trampoline: %@", error); - return completionHandler(error); - }]; - }]; + NSLog(@"setting dns to %@", dns_ip); + NEDNSSettings* dns = [[NEDNSSettings alloc] initWithServers:@[dns_ip]]; + dns.domainName = @"localhost.loki"; + dns.matchDomains = @[@""]; + // In theory, matchDomains is supposed to be set to DNS suffixes that we resolve. This seems + // highly unreliable, though: often it just doesn't work at all (perhaps only if we make + // ourselves the default route?), and even when it does work, it seems there are secret reasons + // that some domains (such as instagram.com) still won't work because there's some magic sauce + // in the OS that Apple engineers don't want to disclose ("This is what I expected, actually. + // Although I will not comment on what I believe is happening here", from + // https://developer.apple.com/forums/thread/685410). + // + // So the documentation sucks and the feature doesn't appear to work, so as much as it would be + // nice to capture only .loki and .snode when not in exit mode, we can't, so capture everything + // and use our default upstream. + dns.matchDomains = @[@""]; + dns.matchDomainsNoSearch = true; + dns.searchDomains = @[]; + settings.DNSSettings = dns; + + NWHostEndpoint* upstreamdns_ep; + if (strlen(conf.upstream_dns)) + upstreamdns_ep = + [NWHostEndpoint endpointWithHostname:[NSString stringWithUTF8String:conf.upstream_dns] + port:@(conf.upstream_dns_port).stringValue]; + + NEIPv4Settings* ipv4 = [[NEIPv4Settings alloc] initWithAddresses:@[ip] subnetMasks:@[mask]]; + tun_route4 = [[NEIPv4Route alloc] initWithDestinationAddress:ip subnetMask:mask]; + ipv4.includedRoutes = @[tun_route4]; + settings.IPv4Settings = ipv4; + + NSString* ip6 = [NSString stringWithUTF8String:conf.tunnel_ipv6_ip]; + NSNumber* ip6_prefix = [NSNumber numberWithUnsignedInt:conf.tunnel_ipv6_prefix]; + NEIPv6Settings* ipv6 = [[NEIPv6Settings alloc] initWithAddresses:@[ip6] + networkPrefixLengths:@[ip6_prefix]]; + tun_route6 = [[NEIPv6Route alloc] initWithDestinationAddress:ip6 + networkPrefixLength:ip6_prefix]; + ipv6.includedRoutes = @[tun_route6]; + settings.IPv6Settings = ipv6; + + __weak LLARPPacketTunnel* weakSelf = self; + [self setTunnelNetworkSettings:settings + completionHandler:^(NSError* err) { + if (err) + { + NSLog(@"Failed to configure lokinet tunnel: %@", err); + return completionHandler(err); + } + LLARPPacketTunnel* strongSelf = weakSelf; + if (!strongSelf) + return completionHandler(nil); + + int start_ret = + llarp_apple_start(strongSelf->lokinet, (__bridge void*)strongSelf); + if (start_ret != 0) + { + NSError* start_failure = + [NSError errorWithDomain:error_domain + code:start_ret + userInfo:@{@"Error": @"Failed to start lokinet"}]; + NSLog(@"%@", start_failure); + lokinet = nil; + return completionHandler(start_failure); + } + + NSString* dns_tramp_ip = @"127.0.0.1"; + NSLog( + @"Starting DNS exit mode trampoline to %@ on %@:%d", + upstreamdns_ep, + dns_tramp_ip, + dns_trampoline_port); + NWUDPSession* upstreamdns = + [strongSelf createUDPSessionThroughTunnelToEndpoint:upstreamdns_ep + fromEndpoint:nil]; + strongSelf->dns_tramp = [LLARPDNSTrampoline alloc]; + [strongSelf->dns_tramp + startWithUpstreamDns:upstreamdns + listenIp:dns_tramp_ip + listenPort:dns_trampoline_port + uvLoop:llarp_apple_get_uv_loop(strongSelf->lokinet) + completionHandler:^(NSError* error) { + if (error) + NSLog(@"Error starting dns trampoline: %@", error); + return completionHandler(error); + }]; + }]; } - (void)stopTunnelWithReason:(NEProviderStopReason)reason completionHandler:(void (^)(void))completionHandler { - if (lokinet) - { - llarp_apple_shutdown(lokinet); - lokinet = nil; - } - completionHandler(); + if (lokinet) + { + llarp_apple_shutdown(lokinet); + lokinet = nil; + } + completionHandler(); } - (void)handleAppMessage:(NSData*)messageData completionHandler:(void (^)(NSData* responseData))completionHandler { - NSData* response = [NSData dataWithBytesNoCopy:"ok" length:3 freeWhenDone:NO]; - completionHandler(response); + NSData* response = [NSData dataWithBytesNoCopy:"ok" length:3 freeWhenDone:NO]; + completionHandler(response); } - (void)updateNetworkSettings { - self.reasserting = YES; - __weak LLARPPacketTunnel* weakSelf = self; - // Apple documentation says that setting network settings to nil isn't required before setting it - // to a new value. Apple lies: both end up with a routing table that looks exactly the same (from - // both `netstat -rn` and from everything that happens in `route -n monitor`), but if we don't - // call with nil first then everything fails to route to either lokinet *and* clearnet through the - // exit, so there is apparently some special magic internal Apple state that actually *does* - // require the tunnel settings being reset with nil first. - // - // Thanks for the accurate documentation, Apple. - // - [self setTunnelNetworkSettings:nil - completionHandler:^(NSError* err) { - if (err) - NSLog(@"Failed to clear lokinet tunnel settings: %@", err); - LLARPPacketTunnel* strongSelf = weakSelf; - if (strongSelf) - { - [weakSelf - setTunnelNetworkSettings:strongSelf->settings - completionHandler:^(NSError* err) { - LLARPPacketTunnel* strongSelf = weakSelf; - if (strongSelf) - strongSelf.reasserting = NO; - if (err) - NSLog(@"Failed to reconfigure lokinet tunnel settings: %@", err); - }]; - } - }]; + self.reasserting = YES; + __weak LLARPPacketTunnel* weakSelf = self; + // Apple documentation says that setting network settings to nil isn't required before setting + // it to a new value. Apple lies: both end up with a routing table that looks exactly the same + // (from both `netstat -rn` and from everything that happens in `route -n monitor`), but if we + // don't call with nil first then everything fails to route to either lokinet *and* clearnet + // through the exit, so there is apparently some special magic internal Apple state that + // actually *does* require the tunnel settings being reset with nil first. + // + // Thanks for the accurate documentation, Apple. + // + [self setTunnelNetworkSettings:nil + completionHandler:^(NSError* err) { + if (err) + NSLog(@"Failed to clear lokinet tunnel settings: %@", err); + LLARPPacketTunnel* strongSelf = weakSelf; + if (strongSelf) + { + [weakSelf setTunnelNetworkSettings:strongSelf->settings + completionHandler:^(NSError* err) { + LLARPPacketTunnel* strongSelf = weakSelf; + if (strongSelf) + strongSelf.reasserting = NO; + if (err) + NSLog( + @"Failed to reconfigure lokinet tunnel settings: " + @"%@", + err); + }]; + } + }]; } @end #ifdef MACOS_SYSTEM_EXTENSION -int -main() +int main() { - [NEProvider startSystemExtensionMode]; - dispatch_main(); + [NEProvider startSystemExtensionMode]; + dispatch_main(); } #endif diff --git a/llarp/apple/context.hpp b/llarp/apple/context.hpp index cc7db2996..9d88d6a9f 100644 --- a/llarp/apple/context.hpp +++ b/llarp/apple/context.hpp @@ -7,22 +7,21 @@ namespace llarp::apple { - struct Context : public llarp::Context - { - std::shared_ptr - makeVPNPlatform() override + struct Context : public llarp::Context { - return std::make_shared( - *this, m_PacketWriter, m_OnReadable, route_callbacks, callback_context); - } + std::shared_ptr makeVPNPlatform() override + { + return std::make_shared( + *this, m_PacketWriter, m_OnReadable, route_callbacks, callback_context); + } - // Callbacks that must be set for packet handling *before* calling Setup/Configure/Run; the main - // point of these is to get passed through to VPNInterface, which will be called during setup, - // after construction. - VPNInterface::packet_write_callback m_PacketWriter; - VPNInterface::on_readable_callback m_OnReadable; - llarp_route_callbacks route_callbacks{}; - void* callback_context = nullptr; - }; + // Callbacks that must be set for packet handling *before* calling Setup/Configure/Run; the + // main point of these is to get passed through to VPNInterface, which will be called during + // setup, after construction. + VPNInterface::packet_write_callback m_PacketWriter; + VPNInterface::on_readable_callback m_OnReadable; + llarp_route_callbacks route_callbacks{}; + void* callback_context = nullptr; + }; } // namespace llarp::apple diff --git a/llarp/apple/context_wrapper.cpp b/llarp/apple/context_wrapper.cpp index 5771cd946..61b523b18 100644 --- a/llarp/apple/context_wrapper.cpp +++ b/llarp/apple/context_wrapper.cpp @@ -20,192 +20,188 @@ namespace { - struct instance_data - { - llarp::apple::Context context; - std::thread runner; - packet_writer_callback packet_writer; - start_reading_callback start_reading; + struct instance_data + { + llarp::apple::Context context; + std::thread runner; + packet_writer_callback packet_writer; + start_reading_callback start_reading; - std::weak_ptr iface; - }; + std::weak_ptr iface; + }; } // namespace // Expose this with C linkage so that objective-c can use it extern "C" const uint16_t dns_trampoline_port = llarp::apple::dns_trampoline_port; -void* -llarp_apple_init(llarp_apple_config* appleconf) +void* llarp_apple_init(llarp_apple_config* appleconf) { - llarp::log::clear_sinks(); - llarp::log::add_sink(std::make_shared( - [](const char* msg, void* nslog) { reinterpret_cast(nslog)(msg); }, - nullptr, - reinterpret_cast(appleconf->ns_logger))); - llarp::logRingBuffer = std::make_shared(100); - llarp::log::add_sink(llarp::logRingBuffer, llarp::log::DEFAULT_PATTERN_MONO); - - try - { - auto config_dir = fs::u8path(appleconf->config_dir); - auto config = std::make_shared(config_dir); - fs::path config_path = config_dir / "lokinet.ini"; - if (!fs::exists(config_path)) - llarp::ensure_config(config_dir, config_path, /*overwrite=*/false, /*asRouter=*/false); - config->load(config_path); - - // If no range is specified then go look for a free one, set that in the config, and then return - // it to the caller via the char* parameters. - auto& range = config->network.if_addr; - if (!range.addr.h) - { - if (auto maybe = llarp::net::Platform::Default_ptr()->FindFreeRange()) - range = *maybe; - else - throw std::runtime_error{"Could not find any free IP range"}; - } - auto addr = llarp::net::TruncateV6(range.addr).ToString(); - auto mask = llarp::net::TruncateV6(range.netmask_bits).ToString(); - if (addr.size() > 15 || mask.size() > 15) - throw std::runtime_error{"Unexpected non-IPv4 tunnel range configured"}; - std::strncpy(appleconf->tunnel_ipv4_ip, addr.c_str(), sizeof(appleconf->tunnel_ipv4_ip)); - std::strncpy( - appleconf->tunnel_ipv4_netmask, mask.c_str(), sizeof(appleconf->tunnel_ipv4_netmask)); - - // TODO: in the future we want to do this properly with our pubkey (see issue #1705), but that's - // going to take a bit more work because we currently can't *get* the (usually) ephemeral pubkey - // at this stage of lokinet configuration. So for now we just stick our IPv4 address into it - // until #1705 gets implemented. - llarp::huint128_t ipv6{ - llarp::uint128_t{0xfd2e'6c6f'6b69'0000, llarp::net::TruncateV6(range.addr).h}}; - std::strncpy( - appleconf->tunnel_ipv6_ip, ipv6.ToString().c_str(), sizeof(appleconf->tunnel_ipv6_ip)); - appleconf->tunnel_ipv6_prefix = 48; - - appleconf->upstream_dns[0] = '\0'; - for (auto& upstream : config->dns.upstream_dns) + llarp::log::clear_sinks(); + llarp::log::add_sink(std::make_shared( + [](const char* msg, void* nslog) { reinterpret_cast(nslog)(msg); }, + nullptr, + reinterpret_cast(appleconf->ns_logger))); + llarp::logRingBuffer = std::make_shared(100); + llarp::log::add_sink(llarp::logRingBuffer, llarp::log::DEFAULT_PATTERN_MONO); + + try { - if (upstream.isIPv4()) - { - std::strcpy(appleconf->upstream_dns, upstream.hostString().c_str()); - appleconf->upstream_dns_port = upstream.getPort(); - break; - } - } + auto config_dir = fs::u8path(appleconf->config_dir); + auto config = std::make_shared(config_dir); + fs::path config_path = config_dir / "lokinet.ini"; + if (!fs::exists(config_path)) + llarp::ensure_config(config_dir, config_path, /*overwrite=*/false, /*asRouter=*/false); + config->load(config_path); + + // If no range is specified then go look for a free one, set that in the config, and then + // return it to the caller via the char* parameters. + auto& range = config->network.if_addr; + if (!range.addr.h) + { + if (auto maybe = llarp::net::Platform::Default_ptr()->FindFreeRange()) + range = *maybe; + else + throw std::runtime_error{"Could not find any free IP range"}; + } + auto addr = llarp::net::TruncateV6(range.addr).ToString(); + auto mask = llarp::net::TruncateV6(range.netmask_bits).ToString(); + if (addr.size() > 15 || mask.size() > 15) + throw std::runtime_error{"Unexpected non-IPv4 tunnel range configured"}; + std::strncpy(appleconf->tunnel_ipv4_ip, addr.c_str(), sizeof(appleconf->tunnel_ipv4_ip)); + std::strncpy( + appleconf->tunnel_ipv4_netmask, mask.c_str(), sizeof(appleconf->tunnel_ipv4_netmask)); + + // TODO: in the future we want to do this properly with our pubkey (see issue #1705), but + // that's going to take a bit more work because we currently can't *get* the (usually) + // ephemeral pubkey at this stage of lokinet configuration. So for now we just stick our + // IPv4 address into it until #1705 gets implemented. + llarp::huint128_t ipv6{ + llarp::uint128_t{0xfd2e'6c6f'6b69'0000, llarp::net::TruncateV6(range.addr).h}}; + std::strncpy( + appleconf->tunnel_ipv6_ip, ipv6.ToString().c_str(), sizeof(appleconf->tunnel_ipv6_ip)); + appleconf->tunnel_ipv6_prefix = 48; + + appleconf->upstream_dns[0] = '\0'; + for (auto& upstream : config->dns.upstream_dns) + { + if (upstream.isIPv4()) + { + std::strcpy(appleconf->upstream_dns, upstream.hostString().c_str()); + appleconf->upstream_dns_port = upstream.getPort(); + break; + } + } #ifdef MACOS_SYSTEM_EXTENSION - std::strncpy( - appleconf->dns_bind_ip, - config->dns.m_bind.front().hostString().c_str(), - sizeof(appleconf->dns_bind_ip)); + std::strncpy( + appleconf->dns_bind_ip, + config->dns.m_bind.front().hostString().c_str(), + sizeof(appleconf->dns_bind_ip)); #endif - // If no explicit bootstrap then set the system default one included with the app bundle - if (config->bootstrap.files.empty()) - config->bootstrap.files.push_back(fs::u8path(appleconf->default_bootstrap)); + // If no explicit bootstrap then set the system default one included with the app bundle + if (config->bootstrap.files.empty()) + config->bootstrap.files.push_back(fs::u8path(appleconf->default_bootstrap)); - auto inst = std::make_unique(); - inst->context.Configure(std::move(config)); - inst->context.route_callbacks = appleconf->route_callbacks; + auto inst = std::make_unique(); + inst->context.Configure(std::move(config)); + inst->context.route_callbacks = appleconf->route_callbacks; - inst->packet_writer = appleconf->packet_writer; - inst->start_reading = appleconf->start_reading; + inst->packet_writer = appleconf->packet_writer; + inst->start_reading = appleconf->start_reading; - return inst.release(); - } - catch (const std::exception& e) - { - llarp::LogError("Failed to initialize lokinet from config: ", e.what()); - } - return nullptr; + return inst.release(); + } + catch (const std::exception& e) + { + llarp::LogError("Failed to initialize lokinet from config: ", e.what()); + } + return nullptr; } -int -llarp_apple_start(void* lokinet, void* callback_context) +int llarp_apple_start(void* lokinet, void* callback_context) { - auto* inst = static_cast(lokinet); + auto* inst = static_cast(lokinet); + + inst->context.callback_context = callback_context; + + inst->context.m_PacketWriter = [inst, callback_context]( + int af_family, void* data, size_t size) { + inst->packet_writer(af_family, data, size, callback_context); + return true; + }; + + inst->context.m_OnReadable = [inst, callback_context](llarp::apple::VPNInterface& iface) { + inst->iface = iface.weak_from_this(); + inst->start_reading(callback_context); + }; + + std::promise result; + inst->runner = std::thread{[inst, &result] { + const llarp::RuntimeOptions opts{}; + try + { + inst->context.Setup(opts); + } + catch (...) + { + result.set_exception(std::current_exception()); + return; + } + result.set_value(); + inst->context.Run(opts); + }}; - inst->context.callback_context = callback_context; - - inst->context.m_PacketWriter = [inst, callback_context](int af_family, void* data, size_t size) { - inst->packet_writer(af_family, data, size, callback_context); - return true; - }; - - inst->context.m_OnReadable = [inst, callback_context](llarp::apple::VPNInterface& iface) { - inst->iface = iface.weak_from_this(); - inst->start_reading(callback_context); - }; - - std::promise result; - inst->runner = std::thread{[inst, &result] { - const llarp::RuntimeOptions opts{}; try { - inst->context.Setup(opts); + result.get_future().get(); } - catch (...) + catch (const std::exception& e) { - result.set_exception(std::current_exception()); - return; + llarp::LogError("Failed to initialize lokinet: ", e.what()); + return -1; } - result.set_value(); - inst->context.Run(opts); - }}; - - try - { - result.get_future().get(); - } - catch (const std::exception& e) - { - llarp::LogError("Failed to initialize lokinet: ", e.what()); - return -1; - } - - return 0; + + return 0; } -uv_loop_t* -llarp_apple_get_uv_loop(void* lokinet) +uv_loop_t* llarp_apple_get_uv_loop(void* lokinet) { - auto& inst = *static_cast(lokinet); - auto uvw = inst.context.loop->MaybeGetUVWLoop(); - assert(uvw); - return uvw->raw(); + auto& inst = *static_cast(lokinet); + auto uvw = inst.context.loop->MaybeGetUVWLoop(); + assert(uvw); + return uvw->raw(); } -int -llarp_apple_incoming(void* lokinet, const llarp_incoming_packet* packets, size_t size) +int llarp_apple_incoming(void* lokinet, const llarp_incoming_packet* packets, size_t size) { - auto& inst = *static_cast(lokinet); - - auto iface = inst.iface.lock(); - if (!iface) - return -1; - - int count = 0; - for (size_t i = 0; i < size; i++) - { - llarp_buffer_t buf{static_cast(packets[i].bytes), packets[i].size}; - if (iface->OfferReadPacket(buf)) - count++; - else - llarp::LogError("invalid IP packet: ", llarp::buffer_printer(buf)); - } - - iface->MaybeWakeUpperLayers(); - return count; + auto& inst = *static_cast(lokinet); + + auto iface = inst.iface.lock(); + if (!iface) + return -1; + + int count = 0; + for (size_t i = 0; i < size; i++) + { + llarp_buffer_t buf{static_cast(packets[i].bytes), packets[i].size}; + if (iface->OfferReadPacket(buf)) + count++; + else + llarp::LogError("invalid IP packet: ", llarp::buffer_printer(buf)); + } + + iface->MaybeWakeUpperLayers(); + return count; } -void -llarp_apple_shutdown(void* lokinet) +void llarp_apple_shutdown(void* lokinet) { - auto* inst = static_cast(lokinet); + auto* inst = static_cast(lokinet); - inst->context.CloseAsync(); - inst->context.Wait(); - inst->runner.join(); - delete inst; + inst->context.CloseAsync(); + inst->context.Wait(); + inst->runner.join(); + delete inst; } diff --git a/llarp/apple/context_wrapper.h b/llarp/apple/context_wrapper.h index a64df18c0..ebee1f599 100644 --- a/llarp/apple/context_wrapper.h +++ b/llarp/apple/context_wrapper.h @@ -12,157 +12,153 @@ extern "C" #include #include - // Port (on localhost) for our DNS trampoline for bouncing DNS requests through the exit route - // when in exit mode. - extern const uint16_t dns_trampoline_port; - - /// C callback function for us to invoke when we need to write a packet - typedef void (*packet_writer_callback)(int af, const void* data, size_t size, void* ctx); - - /// C callback function to invoke once we are ready to start receiving packets - typedef void (*start_reading_callback)(void* ctx); - - /// C callback that bridges things into NSLog - typedef void (*ns_logger_callback)(const char* msg); - - /// C callbacks to add/remove specific and default routes to the tunnel - typedef void (*llarp_route_ipv4_callback)(const char* addr, const char* netmask, void* ctx); - typedef void (*llarp_route_ipv6_callback)(const char* addr, int prefix, void* ctx); - typedef void (*llarp_default_route_callback)(void* ctx); - typedef struct llarp_route_callbacks - { - /// Callback invoked to set up an IPv4 range that should be routed through the tunnel - /// interface. Called with the address and netmask. - llarp_route_ipv4_callback add_ipv4_route; - - /// Callback invoked to set the tunnel as the default IPv4 route. - llarp_default_route_callback add_ipv4_default_route; - - /// Callback invoked to remove a specific range from the tunnel IPv4 routes. Called with the - /// address and netmask. - llarp_route_ipv4_callback del_ipv4_route; - - /// Callback invoked to set up an IPv6 range that should be routed through the tunnel - /// interface. Called with the address and netmask. - llarp_route_ipv6_callback add_ipv6_route; - - /// Callback invoked to remove a specific range from the tunnel IPv6 routes. Called with the - /// address and netmask. - llarp_route_ipv6_callback del_ipv6_route; - - /// Callback invoked to set the tunnel as the default IPv4/IPv6 route. - llarp_default_route_callback add_default_route; - - /// Callback invoked to remove the tunnel as the default IPv4/IPv6 route. - llarp_default_route_callback del_default_route; - } llarp_route_callbacks; - - /// Pack of crap to be passed into llarp_apple_init to initialize - typedef struct llarp_apple_config - { - /// lokinet configuration directory, expected to be the application-specific "home" directory, - /// which is where state files are stored and the lokinet.ini will be loaded (or created if it - /// doesn't exist). - const char* config_dir; - /// path to the default bootstrap.signed file included in installation, which will be used by - /// default when no specific bootstrap is in the config file. - const char* default_bootstrap; - /// llarp_apple_init writes the IP address for the primary tunnel IP address here, - /// null-terminated. - char tunnel_ipv4_ip[INET_ADDRSTRLEN]; - /// llarp_apple_init writes the netmask of the tunnel address here, null-terminated. - char tunnel_ipv4_netmask[INET_ADDRSTRLEN]; - /// Writes the IPv6 address for the tunnel here, null-terminated. - char tunnel_ipv6_ip[INET6_ADDRSTRLEN]; - /// IPv6 address prefix. - uint16_t tunnel_ipv6_prefix; - - /// The first upstream DNS server's IPv4 address the OS should use when in exit mode. - /// (Currently on mac in exit mode we only support querying the first such configured server). - char upstream_dns[INET_ADDRSTRLEN]; - uint16_t upstream_dns_port; + // Port (on localhost) for our DNS trampoline for bouncing DNS requests through the exit route + // when in exit mode. + extern const uint16_t dns_trampoline_port; + + /// C callback function for us to invoke when we need to write a packet + typedef void (*packet_writer_callback)(int af, const void* data, size_t size, void* ctx); + + /// C callback function to invoke once we are ready to start receiving packets + typedef void (*start_reading_callback)(void* ctx); + + /// C callback that bridges things into NSLog + typedef void (*ns_logger_callback)(const char* msg); + + /// C callbacks to add/remove specific and default routes to the tunnel + typedef void (*llarp_route_ipv4_callback)(const char* addr, const char* netmask, void* ctx); + typedef void (*llarp_route_ipv6_callback)(const char* addr, int prefix, void* ctx); + typedef void (*llarp_default_route_callback)(void* ctx); + typedef struct llarp_route_callbacks + { + /// Callback invoked to set up an IPv4 range that should be routed through the tunnel + /// interface. Called with the address and netmask. + llarp_route_ipv4_callback add_ipv4_route; + + /// Callback invoked to set the tunnel as the default IPv4 route. + llarp_default_route_callback add_ipv4_default_route; + + /// Callback invoked to remove a specific range from the tunnel IPv4 routes. Called with + /// the address and netmask. + llarp_route_ipv4_callback del_ipv4_route; + + /// Callback invoked to set up an IPv6 range that should be routed through the tunnel + /// interface. Called with the address and netmask. + llarp_route_ipv6_callback add_ipv6_route; + + /// Callback invoked to remove a specific range from the tunnel IPv6 routes. Called with + /// the address and netmask. + llarp_route_ipv6_callback del_ipv6_route; + + /// Callback invoked to set the tunnel as the default IPv4/IPv6 route. + llarp_default_route_callback add_default_route; + + /// Callback invoked to remove the tunnel as the default IPv4/IPv6 route. + llarp_default_route_callback del_default_route; + } llarp_route_callbacks; + + /// Pack of crap to be passed into llarp_apple_init to initialize + typedef struct llarp_apple_config + { + /// lokinet configuration directory, expected to be the application-specific "home" + /// directory, which is where state files are stored and the lokinet.ini will be loaded (or + /// created if it doesn't exist). + const char* config_dir; + /// path to the default bootstrap.signed file included in installation, which will be used + /// by default when no specific bootstrap is in the config file. + const char* default_bootstrap; + /// llarp_apple_init writes the IP address for the primary tunnel IP address here, + /// null-terminated. + char tunnel_ipv4_ip[INET_ADDRSTRLEN]; + /// llarp_apple_init writes the netmask of the tunnel address here, null-terminated. + char tunnel_ipv4_netmask[INET_ADDRSTRLEN]; + /// Writes the IPv6 address for the tunnel here, null-terminated. + char tunnel_ipv6_ip[INET6_ADDRSTRLEN]; + /// IPv6 address prefix. + uint16_t tunnel_ipv6_prefix; + + /// The first upstream DNS server's IPv4 address the OS should use when in exit mode. + /// (Currently on mac in exit mode we only support querying the first such configured + /// server). + char upstream_dns[INET_ADDRSTRLEN]; + uint16_t upstream_dns_port; #ifdef MACOS_SYSTEM_EXTENSION - /// DNS bind IP; llarp_apple_init writes the lokinet config value here so that we know (in Apple - /// API code) what to set DNS to when lokinet gets turned on. Null terminated. - char dns_bind_ip[INET_ADDRSTRLEN]; + /// DNS bind IP; llarp_apple_init writes the lokinet config value here so that we know (in + /// Apple API code) what to set DNS to when lokinet gets turned on. Null terminated. + char dns_bind_ip[INET_ADDRSTRLEN]; #endif - /// \defgroup callbacks Callbacks - /// Callbacks we invoke for various operations that require glue into the Apple network - /// extension APIs. All of these except for ns_logger are passed the pointer provided to - /// llarp_apple_start when invoked. - /// @{ - - /// simple wrapper around NSLog for lokinet message logging - ns_logger_callback ns_logger; - - /// C function callback that will be called when we need to write a packet to the packet - /// tunnel. Will be passed AF_INET or AF_INET6, a void pointer to the data, and the size of - /// the data in bytes. - packet_writer_callback packet_writer; - - /// C function callback that will be called when lokinet is setup and ready to start receiving - /// packets from the packet tunnel. This should set up the read handler to deliver packets - /// via llarp_apple_incoming. - start_reading_callback start_reading; - - /// Callbacks invoked to add/remove routes to the tunnel. - llarp_route_callbacks route_callbacks; - - /// @} - } llarp_apple_config; - - /// Initializes a lokinet instance by initializing various objects and loading the configuration - /// (if /lokinet.ini exists). Does not actually start lokinet (call llarp_apple_start - /// for that). - /// - /// Returns NULL if there was a problem initializing/loading the configuration, otherwise returns - /// an opaque void pointer that should be passed into the other llarp_apple_* functions. - /// - /// \param config pointer to a llarp_apple_config where we get the various settings needed - /// and return the ip/mask/dns fields needed for the tunnel. - void* - llarp_apple_init(llarp_apple_config* config); - - /// Starts the lokinet instance in a new thread. - /// - /// \param lokinet the void pointer returned by llarp_apple_init - /// - /// \param callback_context Opaque pointer that is passed into the various callbacks provided to - /// llarp_apple_init. This code does nothing with this pointer aside from passing it through to - /// callbacks. - /// - /// \returns 0 on succesful startup, -1 on failure. - int - llarp_apple_start(void* lokinet, void* callback_context); - - /// Returns a pointer to the uv event loop. Must have called llarp_apple_start already. - uv_loop_t* - llarp_apple_get_uv_loop(void* lokinet); - - /// Struct of packet data; a C array of tests gets passed to llarp_apple_incoming - typedef struct llarp_incoming_packet - { - const void* bytes; - size_t size; - } llarp_incoming_packet; - - /// Called to deliver one or more incoming packets from the apple layer into lokinet. Takes a C - /// array of `llarp_incoming_packets` with pointers/sizes set to the individual new packets that - /// have arrived. - /// - /// Returns the number of valid packets on success (which can be less than the number of provided - /// packets, if some failed to parse), or -1 if there is no current active VPNInterface associated - /// with the lokinet instance (which generally means llarp_apple_start wasn't called or failed, or - /// lokinet is in the process of shutting down). - int - llarp_apple_incoming(void* lokinet, const llarp_incoming_packet* packets, size_t size); - - /// Stops a lokinet instance created with `llarp_apple_initialize`. This waits for lokinet to - /// shut down and rejoins the thread. After this call the given pointer is no longer valid. - void - llarp_apple_shutdown(void* lokinet); + /// \defgroup callbacks Callbacks + /// Callbacks we invoke for various operations that require glue into the Apple network + /// extension APIs. All of these except for ns_logger are passed the pointer provided to + /// llarp_apple_start when invoked. + /// @{ + + /// simple wrapper around NSLog for lokinet message logging + ns_logger_callback ns_logger; + + /// C function callback that will be called when we need to write a packet to the packet + /// tunnel. Will be passed AF_INET or AF_INET6, a void pointer to the data, and the size of + /// the data in bytes. + packet_writer_callback packet_writer; + + /// C function callback that will be called when lokinet is setup and ready to start + /// receiving packets from the packet tunnel. This should set up the read handler to + /// deliver packets via llarp_apple_incoming. + start_reading_callback start_reading; + + /// Callbacks invoked to add/remove routes to the tunnel. + llarp_route_callbacks route_callbacks; + + /// @} + } llarp_apple_config; + + /// Initializes a lokinet instance by initializing various objects and loading the configuration + /// (if /lokinet.ini exists). Does not actually start lokinet (call + /// llarp_apple_start for that). + /// + /// Returns NULL if there was a problem initializing/loading the configuration, otherwise + /// returns an opaque void pointer that should be passed into the other llarp_apple_* functions. + /// + /// \param config pointer to a llarp_apple_config where we get the various settings needed + /// and return the ip/mask/dns fields needed for the tunnel. + void* llarp_apple_init(llarp_apple_config* config); + + /// Starts the lokinet instance in a new thread. + /// + /// \param lokinet the void pointer returned by llarp_apple_init + /// + /// \param callback_context Opaque pointer that is passed into the various callbacks provided to + /// llarp_apple_init. This code does nothing with this pointer aside from passing it through to + /// callbacks. + /// + /// \returns 0 on succesful startup, -1 on failure. + int llarp_apple_start(void* lokinet, void* callback_context); + + /// Returns a pointer to the uv event loop. Must have called llarp_apple_start already. + uv_loop_t* llarp_apple_get_uv_loop(void* lokinet); + + /// Struct of packet data; a C array of tests gets passed to llarp_apple_incoming + typedef struct llarp_incoming_packet + { + const void* bytes; + size_t size; + } llarp_incoming_packet; + + /// Called to deliver one or more incoming packets from the apple layer into lokinet. Takes a C + /// array of `llarp_incoming_packets` with pointers/sizes set to the individual new packets that + /// have arrived. + /// + /// Returns the number of valid packets on success (which can be less than the number of + /// provided packets, if some failed to parse), or -1 if there is no current active VPNInterface + /// associated with the lokinet instance (which generally means llarp_apple_start wasn't called + /// or failed, or lokinet is in the process of shutting down). + int llarp_apple_incoming(void* lokinet, const llarp_incoming_packet* packets, size_t size); + + /// Stops a lokinet instance created with `llarp_apple_initialize`. This waits for lokinet to + /// shut down and rejoins the thread. After this call the given pointer is no longer valid. + void llarp_apple_shutdown(void* lokinet); #ifdef __cplusplus } // extern "C" diff --git a/llarp/apple/route_manager.cpp b/llarp/apple/route_manager.cpp index 952dc9479..1059adfbc 100644 --- a/llarp/apple/route_manager.cpp +++ b/llarp/apple/route_manager.cpp @@ -8,98 +8,94 @@ namespace llarp::apple { - void - RouteManager::check_trampoline(bool enable) - { - if (trampoline_active == enable) - return; - auto router = context.router; - if (!router) + void RouteManager::check_trampoline(bool enable) { - LogError("Cannot reconfigure to use DNS trampoline: no router"); - return; - } + if (trampoline_active == enable) + return; + auto router = context.router; + if (!router) + { + LogError("Cannot reconfigure to use DNS trampoline: no router"); + return; + } - std::shared_ptr tun; - router->hidden_service_context().ForEachService([&tun](const auto& /*name*/, const auto ep) { - tun = std::dynamic_pointer_cast(ep); - return !tun; - }); + std::shared_ptr tun; + router->hidden_service_context().ForEachService( + [&tun](const auto& /*name*/, const auto ep) { + tun = std::dynamic_pointer_cast(ep); + return !tun; + }); - if (!tun) - { - LogError("Cannot reconfigure to use DNS trampoline: no tun endpoint found (!?)"); - return; - } + if (!tun) + { + LogError("Cannot reconfigure to use DNS trampoline: no tun endpoint found (!?)"); + return; + } - if (enable) - tun->ReconfigureDNS({SockAddr{127, 0, 0, 1, {dns_trampoline_port}}}); - else - tun->ReconfigureDNS(router->config()->dns.upstream_dns); + if (enable) + tun->ReconfigureDNS({SockAddr{127, 0, 0, 1, {dns_trampoline_port}}}); + else + tun->ReconfigureDNS(router->config()->dns.upstream_dns); - trampoline_active = enable; - } + trampoline_active = enable; + } - void - RouteManager::add_default_route_via_interface(vpn::NetworkInterface&) - { - check_trampoline(true); - if (callback_context and route_callbacks.add_default_route) - route_callbacks.add_default_route(callback_context); - } + void RouteManager::add_default_route_via_interface(vpn::NetworkInterface&) + { + check_trampoline(true); + if (callback_context and route_callbacks.add_default_route) + route_callbacks.add_default_route(callback_context); + } - void - RouteManager::delete_default_route_via_interface(vpn::NetworkInterface&) - { - check_trampoline(false); - if (callback_context and route_callbacks.del_default_route) - route_callbacks.del_default_route(callback_context); - } + void RouteManager::delete_default_route_via_interface(vpn::NetworkInterface&) + { + check_trampoline(false); + if (callback_context and route_callbacks.del_default_route) + route_callbacks.del_default_route(callback_context); + } - void - RouteManager::add_route_via_interface(vpn::NetworkInterface&, IPRange range) - { - check_trampoline(true); - if (callback_context) + void RouteManager::add_route_via_interface(vpn::NetworkInterface&, IPRange range) { - if (range.IsV4()) - { - if (route_callbacks.add_ipv4_route) - route_callbacks.add_ipv4_route( - range.BaseAddressString().c_str(), - net::TruncateV6(range.netmask_bits).ToString().c_str(), - callback_context); - } - else - { - if (route_callbacks.add_ipv6_route) - route_callbacks.add_ipv6_route( - range.BaseAddressString().c_str(), range.HostmaskBits(), callback_context); - } + check_trampoline(true); + if (callback_context) + { + if (range.IsV4()) + { + if (route_callbacks.add_ipv4_route) + route_callbacks.add_ipv4_route( + range.BaseAddressString().c_str(), + net::TruncateV6(range.netmask_bits).ToString().c_str(), + callback_context); + } + else + { + if (route_callbacks.add_ipv6_route) + route_callbacks.add_ipv6_route( + range.BaseAddressString().c_str(), range.HostmaskBits(), callback_context); + } + } } - } - void - RouteManager::delete_route_via_interface(vpn::NetworkInterface&, IPRange range) - { - check_trampoline(false); - if (callback_context) + void RouteManager::delete_route_via_interface(vpn::NetworkInterface&, IPRange range) { - if (range.IsV4()) - { - if (route_callbacks.del_ipv4_route) - route_callbacks.del_ipv4_route( - range.BaseAddressString().c_str(), - net::TruncateV6(range.netmask_bits).ToString().c_str(), - callback_context); - } - else - { - if (route_callbacks.del_ipv6_route) - route_callbacks.del_ipv6_route( - range.BaseAddressString().c_str(), range.HostmaskBits(), callback_context); - } + check_trampoline(false); + if (callback_context) + { + if (range.IsV4()) + { + if (route_callbacks.del_ipv4_route) + route_callbacks.del_ipv4_route( + range.BaseAddressString().c_str(), + net::TruncateV6(range.netmask_bits).ToString().c_str(), + callback_context); + } + else + { + if (route_callbacks.del_ipv6_route) + route_callbacks.del_ipv6_route( + range.BaseAddressString().c_str(), range.HostmaskBits(), callback_context); + } + } } - } } // namespace llarp::apple diff --git a/llarp/apple/route_manager.hpp b/llarp/apple/route_manager.hpp index cd3e418a1..7cf37eb2a 100644 --- a/llarp/apple/route_manager.hpp +++ b/llarp/apple/route_manager.hpp @@ -7,51 +7,44 @@ namespace llarp::apple { - class RouteManager final : public llarp::vpn::AbstractRouteManager - { - public: - RouteManager(llarp::Context& ctx, llarp_route_callbacks rcs, void* callback_context) - : context{ctx}, callback_context{callback_context}, route_callbacks{std::move(rcs)} - {} + class RouteManager final : public llarp::vpn::AbstractRouteManager + { + public: + RouteManager(llarp::Context& ctx, llarp_route_callbacks rcs, void* callback_context) + : context{ctx}, callback_context{callback_context}, route_callbacks{std::move(rcs)} + {} - /// These are called for poking route holes, but we don't have to do that at all on macos - /// because the appex isn't subject to its own rules. - void - add_route(oxen::quic::Address /*ip*/, oxen::quic::Address /*gateway*/) override - {} + /// These are called for poking route holes, but we don't have to do that at all on macos + /// because the appex isn't subject to its own rules. + void add_route(oxen::quic::Address /*ip*/, oxen::quic::Address /*gateway*/) override + {} - void - delete_route(oxen::quic::Address /*ip*/, oxen::quic::Address /*gateway*/) override - {} + void delete_route(oxen::quic::Address /*ip*/, oxen::quic::Address /*gateway*/) override + {} - void - add_default_route_via_interface(vpn::NetworkInterface& vpn) override; + void add_default_route_via_interface(vpn::NetworkInterface& vpn) override; - void - delete_default_route_via_interface(vpn::NetworkInterface& vpn) override; + void delete_default_route_via_interface(vpn::NetworkInterface& vpn) override; - void - add_route_via_interface(vpn::NetworkInterface& vpn, IPRange range) override; + void add_route_via_interface(vpn::NetworkInterface& vpn, IPRange range) override; - void - delete_route_via_interface(vpn::NetworkInterface& vpn, IPRange range) override; + void delete_route_via_interface(vpn::NetworkInterface& vpn, IPRange range) override; - std::vector - get_non_interface_gateways(vpn::NetworkInterface& /*vpn*/) override - { - // We can't get this on mac from our sandbox, but we don't actually need it because we - // ignore the gateway for AddRoute/DelRoute anyway, so just return a zero IP. - return std::vector{}; - } - - private: - llarp::Context& context; - bool trampoline_active = false; - void - check_trampoline(bool enable); - - void* callback_context = nullptr; - llarp_route_callbacks route_callbacks; - }; + std::vector get_non_interface_gateways( + vpn::NetworkInterface& /*vpn*/) override + { + // We can't get this on mac from our sandbox, but we don't actually need it because we + // ignore the gateway for AddRoute/DelRoute anyway, so just return a zero IP. + return std::vector{}; + } + + private: + llarp::Context& context; + bool trampoline_active = false; + void check_trampoline(bool enable); + + void* callback_context = nullptr; + llarp_route_callbacks route_callbacks; + }; } // namespace llarp::apple diff --git a/llarp/apple/vpn_interface.cpp b/llarp/apple/vpn_interface.cpp index 03964d403..13187b657 100644 --- a/llarp/apple/vpn_interface.cpp +++ b/llarp/apple/vpn_interface.cpp @@ -7,55 +7,50 @@ namespace llarp::apple { - VPNInterface::VPNInterface( - Context& ctx, - packet_write_callback packet_writer, - on_readable_callback on_readable, - Router* router) - : vpn::NetworkInterface{{}} - , m_PacketWriter{std::move(packet_writer)} - , m_OnReadable{std::move(on_readable)} - , _router{router} - { - ctx.loop->call_soon([this] { m_OnReadable(*this); }); - } - - bool - VPNInterface::OfferReadPacket(const llarp_buffer_t& buf) - { - llarp::net::IPPacket pkt; - if (!pkt.Load(buf)) - return false; - m_ReadQueue.tryPushBack(std::move(pkt)); - return true; - } - - void - VPNInterface::MaybeWakeUpperLayers() const - { - _router->TriggerPump(); - } - - int - VPNInterface::PollFD() const - { - return -1; - } - - net::IPPacket - VPNInterface::ReadNextPacket() - { - net::IPPacket pkt{}; - if (not m_ReadQueue.empty()) - pkt = m_ReadQueue.popFront(); - return pkt; - } - - bool - VPNInterface::WritePacket(net::IPPacket pkt) - { - int af_family = pkt.IsV6() ? AF_INET6 : AF_INET; - return m_PacketWriter(af_family, pkt.data(), pkt.size()); - } + VPNInterface::VPNInterface( + Context& ctx, + packet_write_callback packet_writer, + on_readable_callback on_readable, + Router* router) + : vpn::NetworkInterface{{}}, + m_PacketWriter{std::move(packet_writer)}, + m_OnReadable{std::move(on_readable)}, + _router{router} + { + ctx.loop->call_soon([this] { m_OnReadable(*this); }); + } + + bool VPNInterface::OfferReadPacket(const llarp_buffer_t& buf) + { + llarp::net::IPPacket pkt; + if (!pkt.Load(buf)) + return false; + m_ReadQueue.tryPushBack(std::move(pkt)); + return true; + } + + void VPNInterface::MaybeWakeUpperLayers() const + { + _router->TriggerPump(); + } + + int VPNInterface::PollFD() const + { + return -1; + } + + net::IPPacket VPNInterface::ReadNextPacket() + { + net::IPPacket pkt{}; + if (not m_ReadQueue.empty()) + pkt = m_ReadQueue.popFront(); + return pkt; + } + + bool VPNInterface::WritePacket(net::IPPacket pkt) + { + int af_family = pkt.IsV6() ? AF_INET6 : AF_INET; + return m_PacketWriter(af_family, pkt.data(), pkt.size()); + } } // namespace llarp::apple diff --git a/llarp/apple/vpn_interface.hpp b/llarp/apple/vpn_interface.hpp index ee5dfc8dc..55e235d0a 100644 --- a/llarp/apple/vpn_interface.hpp +++ b/llarp/apple/vpn_interface.hpp @@ -8,50 +8,45 @@ namespace llarp::apple { - struct Context; + struct Context; - class VPNInterface final : public vpn::NetworkInterface, - public std::enable_shared_from_this - { - public: - using packet_write_callback = std::function; - using on_readable_callback = std::function; + class VPNInterface final : public vpn::NetworkInterface, + public std::enable_shared_from_this + { + public: + using packet_write_callback = std::function; + using on_readable_callback = std::function; - explicit VPNInterface( - Context& ctx, - packet_write_callback packet_writer, - on_readable_callback on_readable, - Router* router); + explicit VPNInterface( + Context& ctx, + packet_write_callback packet_writer, + on_readable_callback on_readable, + Router* router); - // Method to call when a packet has arrived to deliver the packet to lokinet - bool - OfferReadPacket(const llarp_buffer_t& buf); + // Method to call when a packet has arrived to deliver the packet to lokinet + bool OfferReadPacket(const llarp_buffer_t& buf); - int - PollFD() const override; + int PollFD() const override; - net::IPPacket - ReadNextPacket() override; + net::IPPacket ReadNextPacket() override; - bool - WritePacket(net::IPPacket pkt) override; + bool WritePacket(net::IPPacket pkt) override; - void - MaybeWakeUpperLayers() const override; + void MaybeWakeUpperLayers() const override; - private: - // Function for us to call when we have a packet to emit. Should return true if the packet was - // handed off to the OS successfully. - packet_write_callback m_PacketWriter; + private: + // Function for us to call when we have a packet to emit. Should return true if the packet + // was handed off to the OS successfully. + packet_write_callback m_PacketWriter; - // Called when we are ready to start reading packets - on_readable_callback m_OnReadable; + // Called when we are ready to start reading packets + on_readable_callback m_OnReadable; - static inline constexpr auto PacketQueueSize = 1024; + static inline constexpr auto PacketQueueSize = 1024; - thread::Queue m_ReadQueue{PacketQueueSize}; + thread::Queue m_ReadQueue{PacketQueueSize}; - Router* const _router; - }; + Router* const _router; + }; } // namespace llarp::apple diff --git a/llarp/apple/vpn_platform.cpp b/llarp/apple/vpn_platform.cpp index 7e05cbb05..38ebeb4c7 100644 --- a/llarp/apple/vpn_platform.cpp +++ b/llarp/apple/vpn_platform.cpp @@ -4,21 +4,21 @@ namespace llarp::apple { - VPNPlatform::VPNPlatform( - Context& ctx, - VPNInterface::packet_write_callback packet_writer, - VPNInterface::on_readable_callback on_readable, - llarp_route_callbacks route_callbacks, - void* callback_context) - : _context{ctx} - , _route_manager{ctx, std::move(route_callbacks), callback_context} - , _packet_writer{std::move(packet_writer)} - , _read_cb{std::move(on_readable)} - {} + VPNPlatform::VPNPlatform( + Context& ctx, + VPNInterface::packet_write_callback packet_writer, + VPNInterface::on_readable_callback on_readable, + llarp_route_callbacks route_callbacks, + void* callback_context) + : _context{ctx}, + _route_manager{ctx, std::move(route_callbacks), callback_context}, + _packet_writer{std::move(packet_writer)}, + _read_cb{std::move(on_readable)} + {} - std::shared_ptr - VPNPlatform::ObtainInterface(vpn::InterfaceInfo, Router* router) - { - return std::make_shared(_context, _packet_writer, _read_cb, router); - } + std::shared_ptr VPNPlatform::ObtainInterface( + vpn::InterfaceInfo, Router* router) + { + return std::make_shared(_context, _packet_writer, _read_cb, router); + } } // namespace llarp::apple diff --git a/llarp/apple/vpn_platform.hpp b/llarp/apple/vpn_platform.hpp index 1f5bb1ef6..ab9848c40 100644 --- a/llarp/apple/vpn_platform.hpp +++ b/llarp/apple/vpn_platform.hpp @@ -7,30 +7,29 @@ namespace llarp::apple { - class VPNPlatform final : public vpn::Platform - { - public: - explicit VPNPlatform( - Context& ctx, - VPNInterface::packet_write_callback packet_writer, - VPNInterface::on_readable_callback on_readable, - llarp_route_callbacks route_callbacks, - void* callback_context); + class VPNPlatform final : public vpn::Platform + { + public: + explicit VPNPlatform( + Context& ctx, + VPNInterface::packet_write_callback packet_writer, + VPNInterface::on_readable_callback on_readable, + llarp_route_callbacks route_callbacks, + void* callback_context); - std::shared_ptr - ObtainInterface(vpn::InterfaceInfo, Router*) override; + std::shared_ptr ObtainInterface( + vpn::InterfaceInfo, Router*) override; - vpn::AbstractRouteManager& - RouteManager() override - { - return _route_manager; - } + vpn::AbstractRouteManager& RouteManager() override + { + return _route_manager; + } - private: - Context& _context; - apple::RouteManager _route_manager; - VPNInterface::packet_write_callback _packet_writer; - VPNInterface::on_readable_callback _read_cb; - }; + private: + Context& _context; + apple::RouteManager _route_manager; + VPNInterface::packet_write_callback _packet_writer; + VPNInterface::on_readable_callback _read_cb; + }; } // namespace llarp::apple diff --git a/llarp/bootstrap.cpp b/llarp/bootstrap.cpp index 061e3b3b3..f7ffea431 100644 --- a/llarp/bootstrap.cpp +++ b/llarp/bootstrap.cpp @@ -6,164 +6,157 @@ namespace llarp { - bool - BootstrapList::bt_decode(std::string_view buf) - { - const auto& f = buf.front(); - - switch (f) - { - case 'l': - return bt_decode(oxenc::bt_list_consumer{buf}); - case 'd': - return bt_decode(oxenc::bt_dict_consumer{buf}); - default: - log::critical(logcat, "Unable to parse bootstrap as bt list or dict!"); - return false; - } - } - - bool - BootstrapList::bt_decode(oxenc::bt_list_consumer btlc) - { - try - { - while (not btlc.is_finished()) - emplace(btlc.consume_dict_data()); - } - catch (...) + bool BootstrapList::bt_decode(std::string_view buf) { - log::warning(logcat, "Unable to decode bootstrap RemoteRC"); - return false; + const auto& f = buf.front(); + + switch (f) + { + case 'l': + return bt_decode(oxenc::bt_list_consumer{buf}); + case 'd': + return bt_decode(oxenc::bt_dict_consumer{buf}); + default: + log::critical(logcat, "Unable to parse bootstrap as bt list or dict!"); + return false; + } } - _curr = begin(); - return true; - } - - bool - BootstrapList::bt_decode(oxenc::bt_dict_consumer btdc) - { - try - { - emplace(btdc); - } - catch (const std::exception& e) + bool BootstrapList::bt_decode(oxenc::bt_list_consumer btlc) { - log::warning(logcat, "Unable to decode bootstrap RemoteRC: {}", e.what()); - return false; + try + { + while (not btlc.is_finished()) + emplace(btlc.consume_dict_data()); + } + catch (...) + { + log::warning(logcat, "Unable to decode bootstrap RemoteRC"); + return false; + } + + _curr = begin(); + return true; } - _curr = begin(); - return true; - } - - bool - BootstrapList::contains(const RouterID& rid) const - { - for (const auto& it : *this) + bool BootstrapList::bt_decode(oxenc::bt_dict_consumer btdc) { - if (it.router_id() == rid) + try + { + emplace(btdc); + } + catch (const std::exception& e) + { + log::warning(logcat, "Unable to decode bootstrap RemoteRC: {}", e.what()); + return false; + } + + _curr = begin(); return true; } - return false; - } - - bool - BootstrapList::contains(const RemoteRC& rc) const - { - return count(rc); - } - - std::string_view - BootstrapList::bt_encode() const - { - oxenc::bt_list_producer btlp{}; - - for (const auto& it : *this) - btlp.append(it.view()); + bool BootstrapList::contains(const RouterID& rid) const + { + for (const auto& it : *this) + { + if (it.router_id() == rid) + return true; + } - return btlp.view(); - } + return false; + } - void - BootstrapList::populate_bootstraps( - std::vector paths, const fs::path& def, bool load_fallbacks) - { - for (const auto& f : paths) + bool BootstrapList::contains(const RemoteRC& rc) const { - // TESTNET: TODO: revise fucked config - log::debug(logcat, "Loading BootstrapRC from file at path:{}", f); - if (not read_from_file(f)) - throw std::invalid_argument{"User-provided BootstrapRC is invalid!"}; + return count(rc); } - if (empty()) + std::string_view BootstrapList::bt_encode() const { - log::debug( - logcat, - "BootstrapRC list empty; looking for default BootstrapRC from file at path:{}", - def); - read_from_file(def); + oxenc::bt_list_producer btlp{}; + + for (const auto& it : *this) + btlp.append(it.view()); + + return btlp.view(); } - for (auto itr = begin(); itr != end(); ++itr) + void BootstrapList::populate_bootstraps( + std::vector paths, const fs::path& def, bool load_fallbacks) { - if (RouterContact::is_obsolete(*itr)) // can move this into ::read_from_file - { - log::critical(logcat, "Deleting obsolete BootstrapRC (rid:{})", itr->router_id()); - itr = erase(itr); - continue; - } + for (const auto& f : paths) + { + // TESTNET: TODO: revise fucked config + log::debug(logcat, "Loading BootstrapRC from file at path:{}", f); + if (not read_from_file(f)) + throw std::invalid_argument{"User-provided BootstrapRC is invalid!"}; + } + + if (empty()) + { + log::debug( + logcat, + "BootstrapRC list empty; looking for default BootstrapRC from file at path:{}", + def); + read_from_file(def); + } + + for (auto itr = begin(); itr != end(); ++itr) + { + if (RouterContact::is_obsolete(*itr)) // can move this into ::read_from_file + { + log::critical(logcat, "Deleting obsolete BootstrapRC (rid:{})", itr->router_id()); + itr = erase(itr); + continue; + } + } + + if (empty() and load_fallbacks) + { + log::critical(logcat, "BootstrapRC list empty; loading fallbacks..."); + auto fallbacks = llarp::load_bootstrap_fallbacks(); + + if (auto itr = fallbacks.find(RouterContact::ACTIVE_NETID); itr != fallbacks.end()) + { + log::critical( + logcat, "Loading {} default fallback bootstrap router(s)!", itr->second.size()); + merge(itr->second); + } + + if (empty()) + { + log::error( + logcat, + "No Bootstrap routers were loaded. The default Bootstrap file {} does not " + "exist, and " + "loading fallback Bootstrap RCs failed.", + def); + + throw std::runtime_error("No Bootstrap nodes available."); + } + } + + log::critical(logcat, "We have {} Bootstrap router(s)!", size()); + _curr = begin(); } - if (empty() and load_fallbacks) + bool BootstrapList::read_from_file(const fs::path& fpath) { - log::critical(logcat, "BootstrapRC list empty; loading fallbacks..."); - auto fallbacks = llarp::load_bootstrap_fallbacks(); + bool result = false; - if (auto itr = fallbacks.find(RouterContact::ACTIVE_NETID); itr != fallbacks.end()) - { - log::critical( - logcat, "Loading {} default fallback bootstrap router(s)!", itr->second.size()); - merge(itr->second); - } - - if (empty()) - { - log::error( - logcat, - "No Bootstrap routers were loaded. The default Bootstrap file {} does not exist, and " - "loading fallback Bootstrap RCs failed.", - def); - - throw std::runtime_error("No Bootstrap nodes available."); - } - } + if (not fs::exists(fpath)) + { + log::critical(logcat, "Bootstrap RC file non-existant at path:{}", fpath); + return result; + } - log::critical(logcat, "We have {} Bootstrap router(s)!", size()); - _curr = begin(); - } + auto content = util::file_to_string(fpath); + result = bt_decode(content); - bool - BootstrapList::read_from_file(const fs::path& fpath) - { - bool result = false; + log::critical( + logcat, "{}uccessfully loaded BootstrapRC file at path:{}", result ? "S" : "Un", fpath); - if (not fs::exists(fpath)) - { - log::critical(logcat, "Bootstrap RC file non-existant at path:{}", fpath); - return result; + _curr = begin(); + return result; } - - auto content = util::file_to_string(fpath); - result = bt_decode(content); - - log::critical( - logcat, "{}uccessfully loaded BootstrapRC file at path:{}", result ? "S" : "Un", fpath); - - _curr = begin(); - return result; - } } // namespace llarp diff --git a/llarp/bootstrap.hpp b/llarp/bootstrap.hpp index 86cf416f0..4154a4cf1 100644 --- a/llarp/bootstrap.hpp +++ b/llarp/bootstrap.hpp @@ -10,71 +10,60 @@ namespace llarp { - struct BootstrapList final : public std::set - { - std::set::iterator _curr = begin(); - - const RemoteRC& - current() + struct BootstrapList final : public std::set { - return *_curr; - } + std::set::iterator _curr = begin(); - bool - bt_decode(std::string_view buf); + const RemoteRC& current() + { + return *_curr; + } - bool - bt_decode(oxenc::bt_list_consumer btlc); + bool bt_decode(std::string_view buf); - bool - bt_decode(oxenc::bt_dict_consumer btdc); + bool bt_decode(oxenc::bt_list_consumer btlc); - std::string_view - bt_encode() const; + bool bt_decode(oxenc::bt_dict_consumer btdc); - void - populate_bootstraps(std::vector paths, const fs::path& def, bool load_fallbacks); + std::string_view bt_encode() const; - bool - read_from_file(const fs::path& fpath); + void populate_bootstraps( + std::vector paths, const fs::path& def, bool load_fallbacks); - bool - contains(const RouterID& rid) const; + bool read_from_file(const fs::path& fpath); - // returns a reference to the next index and a boolean that equals true if - // this is the front of the set - const RemoteRC& - next() - { - if (size() < 2) - return *_curr; + bool contains(const RouterID& rid) const; - ++_curr; + // returns a reference to the next index and a boolean that equals true if + // this is the front of the set + const RemoteRC& next() + { + if (size() < 2) + return *_curr; - if (_curr == this->end()) - _curr = this->begin(); + ++_curr; - return *_curr; - } + if (_curr == this->end()) + _curr = this->begin(); - bool - contains(const RemoteRC& rc) const; + return *_curr; + } - void - randomize() - { - if (size() > 1) - _curr = std::next(begin(), std::uniform_int_distribution{0, size() - 1}(csrng)); - } + bool contains(const RemoteRC& rc) const; - void - clear_list() - { - clear(); - } - }; + void randomize() + { + if (size() > 1) + _curr = + std::next(begin(), std::uniform_int_distribution{0, size() - 1}(csrng)); + } + + void clear_list() + { + clear(); + } + }; - std::unordered_map - load_bootstrap_fallbacks(); + std::unordered_map load_bootstrap_fallbacks(); } // namespace llarp diff --git a/llarp/bootstrap_fallbacks.cpp b/llarp/bootstrap_fallbacks.cpp index 79f6f646f..c45f2fe12 100644 --- a/llarp/bootstrap_fallbacks.cpp +++ b/llarp/bootstrap_fallbacks.cpp @@ -4,25 +4,24 @@ namespace llarp { - using namespace std::literals; + using namespace std::literals; - std::unordered_map - load_bootstrap_fallbacks() - { - std::unordered_map fallbacks; - - for (const auto& [network, bootstrap] : - std::initializer_list>{ - // - }) + std::unordered_map load_bootstrap_fallbacks() { - if (network != RouterContact::ACTIVE_NETID) - continue; + std::unordered_map fallbacks; - auto& bsl = fallbacks[network]; - bsl.bt_decode(bootstrap); - } + for (const auto& [network, bootstrap] : + std::initializer_list>{ + // + }) + { + if (network != RouterContact::ACTIVE_NETID) + continue; - return fallbacks; - } + auto& bsl = fallbacks[network]; + bsl.bt_decode(bootstrap); + } + + return fallbacks; + } } // namespace llarp diff --git a/llarp/config/config.cpp b/llarp/config/config.cpp index eeb2fecbf..a72615c80 100644 --- a/llarp/config/config.cpp +++ b/llarp/config/config.cpp @@ -20,1594 +20,1620 @@ namespace llarp { - using namespace config; - namespace - { - struct ConfigGenParameters_impl : public ConfigGenParameters + using namespace config; + namespace { - const llarp::net::Platform* - Net_ptr() const override - { - return llarp::net::Platform::Default_ptr(); - } - }; - } // namespace - - void - RouterConfig::define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params) - { - constexpr Default DefaultJobQueueSize{1024 * 8}; - constexpr Default DefaultWorkerThreads{0}; - constexpr Default DefaultBlockBogons{true}; - - conf.define_option( - "router", "job-queue-size", DefaultJobQueueSize, Hidden, [this](int arg) { - if (arg < 1024) - throw std::invalid_argument("job-queue-size must be 1024 or greater"); - - job_que_size = arg; - }); - - conf.define_option( - "router", - "netid", - Default{llarp::LOKINET_DEFAULT_NETID}, - Comment{ - "Network ID; this is '"s + llarp::LOKINET_DEFAULT_NETID + "' for mainnet, '"s - + llarp::LOKINET_TESTNET_NETID + "' for testnet."s, - }, - [this](std::string arg) { - if (arg.size() > NETID_SIZE) - throw std::invalid_argument{ - fmt::format("netid is too long, max length is {}", NETID_SIZE)}; - - net_id = std::move(arg); - }); - - conf.define_option( - "router", - "relay-connections", - Default{CLIENT_ROUTER_CONNECTIONS}, - ClientOnly, - Comment{ - "Minimum number of routers lokinet client will attempt to maintain connections to.", - }, - [=](int arg) { - if (arg < CLIENT_ROUTER_CONNECTIONS) - throw std::invalid_argument{ - fmt::format("Client relay connections must be >= {}", CLIENT_ROUTER_CONNECTIONS)}; - - client_router_connections = arg; - }); - - conf.define_option( - "router", - "min-connections", - Deprecated, - Comment{ - "Minimum number of routers lokinet will attempt to maintain connections to.", - }, - [=](int) { - log::warning( - logcat, "Router min-connections is deprecated; use relay-connections for clients"); - }); - - conf.define_option( - "router", - "max-connections", - Deprecated, - Comment{ - "Maximum number (hard limit) of routers lokinet will be connected to at any time.", - }, - [=](int) { - log::warning( - logcat, "Router max-connections is deprecated; use relay-connections for clients"); - }); - - conf.define_option("router", "nickname", Deprecated); - - conf.define_option( - "router", - "data-dir", - Default{params.default_data_dir}, - Comment{ - "Optional directory for containing lokinet runtime data. This includes generated", - "private keys.", - }, - [this](fs::path arg) { - if (arg.empty()) - throw std::invalid_argument("[router]:data-dir is empty"); - if (not fs::exists(arg)) - throw std::runtime_error{ - fmt::format("Specified [router]:data-dir {} does not exist", arg)}; - - data_dir = std::move(arg); - }); - - conf.define_option( - "router", - "public-ip", - RelayOnly, - Comment{ - "For complex network configurations where the detected IP is incorrect or non-public", - "this setting specifies the public IP at which this router is reachable. When", - "provided the public-port option must also be specified.", - }, - [this](std::string arg) { public_ip = std::move(arg); }); - - conf.define_option("router", "public-address", Hidden, [](std::string) { - throw std::invalid_argument{ - "[router]:public-address option no longer supported, use [router]:public-ip and " - "[router]:public-port instead"}; - }); - - conf.define_option( - "router", - "public-port", - RelayOnly, - Comment{ - "When specifying public-ip=, this specifies the public UDP port at which this lokinet", - "router is reachable. Required when public-ip is used.", - }, - [this](uint16_t arg) { - if (arg <= 0 || arg > std::numeric_limits::max()) - throw std::invalid_argument("public-port must be >= 0 and <= 65536"); - public_port = arg; - }); - - conf.define_option( - "router", - "worker-threads", - DefaultWorkerThreads, - Comment{ - "The number of threads available for performing cryptographic functions.", - "The minimum is one thread, but network performance may increase with more.", - "threads. Should not exceed the number of logical CPU cores.", - "0 means use the number of logical CPU cores detected at startup.", - }, - [this](int arg) { - if (arg < 0) - throw std::invalid_argument("worker-threads must be >= 0"); - - worker_threads = arg; - }); - - // Hidden option because this isn't something that should ever be turned off occasionally when - // doing dev/testing work. - conf.define_option( - "router", "block-bogons", DefaultBlockBogons, Hidden, assignment_acceptor(block_bogons)); - - constexpr auto relative_to_datadir = - "An absolute path is used as-is, otherwise relative to 'data-dir'."; - - conf.define_option( - "router", - "contact-file", - RelayOnly, - Default{llarp::our_rc_filename}, - assignment_acceptor(rc_file), - Comment{ - "Filename in which to store the router contact file", - relative_to_datadir, - }); - - conf.define_option( - "router", - "encryption-privkey", - RelayOnly, - Default{llarp::our_enc_key_filename}, - assignment_acceptor(enckey_file), - Comment{ - "Filename in which to store the encryption private key", - relative_to_datadir, - }); - - conf.define_option( - "router", - "ident-privkey", - RelayOnly, - Default{llarp::our_identity_filename}, - assignment_acceptor(idkey_file), - Comment{ - "Filename in which to store the identity private key", - relative_to_datadir, - }); - - conf.define_option( - "router", - "transport-privkey", - RelayOnly, - Default{llarp::our_transport_key_filename}, - assignment_acceptor(transkey_file), - Comment{ - "Filename in which to store the transport private key.", - relative_to_datadir, - }); - - // Deprecated options: - - // these weren't even ever used! - conf.define_option("router", "max-routers", Deprecated); - conf.define_option("router", "min-routers", Deprecated); - - // TODO: this may have been a synonym for [router]worker-threads - conf.define_option("router", "threads", Deprecated); - conf.define_option("router", "net-threads", Deprecated); - - is_relay = params.is_relay; - } - - void - NetworkConfig::define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params) - { - (void)params; - - static constexpr Default ProfilingValueDefault{true}; - static constexpr Default SaveProfilesDefault{true}; - static constexpr Default ReachableDefault{true}; - static constexpr Default HopsDefault{4}; - static constexpr Default PathsDefault{6}; - static constexpr Default IP6RangeDefault{"fd00::"}; - - conf.define_option( - "network", "type", Default{"tun"}, Hidden, assignment_acceptor(endpoint_type)); - - conf.define_option( - "network", - "save-profiles", - SaveProfilesDefault, - Hidden, - assignment_acceptor(save_profiles)); - - conf.define_option( - "network", - "profiling", - ProfilingValueDefault, - Hidden, - assignment_acceptor(enable_profiling)); - - conf.define_option("network", "profiles", Deprecated); - - conf.define_option( - "network", - "strict-connect", - ClientOnly, - MultiValue, - [this](std::string value) { - RouterID router; - if (not router.from_string(value)) - throw std::invalid_argument{"bad snode value: " + value}; - if (not strict_connect.insert(router).second) - throw std::invalid_argument{"duplicate strict connect snode: " + value}; - }, - Comment{ - "Public keys of routers which will act as pinned first-hops. This may be used to", - "provide a trusted router (consider that you are not fully anonymous with your", - "first hop). This REQUIRES two or more nodes to be specified.", - }); - - conf.define_option( - "network", - "keyfile", - ClientOnly, - assignment_acceptor(keyfile), - Comment{ - "The private key to persist address with. If not specified the address will be", - "ephemeral.", - }); - - conf.define_option( - "network", - "auth", - ClientOnly, - Comment{ - "Set the endpoint authentication mechanism.", - "none/whitelist/lmq/file", - }, - [this](std::string arg) { - if (arg.empty()) - return; - auth_type = service::parse_auth_type(arg); - }); - - conf.define_option( - "network", - "auth-lmq", - ClientOnly, - assignment_acceptor(auth_url), - Comment{ - "lmq endpoint to talk to for authenticating new sessions", - "ipc:///var/lib/lokinet/auth.socket", - "tcp://127.0.0.1:5555", - }); - - conf.define_option( - "network", - "auth-lmq-method", - ClientOnly, - Default{"llarp.auth"}, - Comment{ - "lmq function to call for authenticating new sessions", - "llarp.auth", - }, - [this](std::string arg) { - if (arg.empty()) - return; - auth_method = std::move(arg); - }); - - conf.define_option( - "network", - "auth-whitelist", - ClientOnly, - MultiValue, - Comment{ - "manually add a remote endpoint by .loki address to the access whitelist", - }, - [this](std::string arg) { - service::Address addr; - if (not addr.FromString(arg)) - throw std::invalid_argument{fmt::format("bad loki address: {}", arg)}; - auth_whitelist.emplace(std::move(addr)); - }); - - conf.define_option( - "network", - "auth-file", - ClientOnly, - MultiValue, - Comment{ - "Read auth tokens from file to accept endpoint auth", - "Can be provided multiple times", - }, - [this](fs::path arg) { - if (not fs::exists(arg)) - throw std::invalid_argument{ - fmt::format("cannot load auth file {}: file does not exist", arg)}; - auth_files.emplace(std::move(arg)); - }); - conf.define_option( - "network", - "auth-file-type", - ClientOnly, - Comment{ - "How to interpret the contents of an auth file.", - "Possible values: hashes, plaintext", - }, - [this](std::string arg) { - auth_file_type = service::parse_auth_file_type(std::move(arg)); - }); - - conf.define_option( - "network", - "auth-static", - ClientOnly, - MultiValue, - Comment{ - "Manually add a static auth code to accept for endpoint auth", - "Can be provided multiple times", - }, - [this](std::string arg) { auth_static_tokens.emplace(std::move(arg)); }); - - conf.define_option( - "network", - "reachable", - ClientOnly, - ReachableDefault, - assignment_acceptor(is_reachable), - Comment{ - "Determines whether we will pubish our snapp's introset to the DHT.", - }); - - conf.define_option( - "network", - "hops", - HopsDefault, - Comment{ - "Number of hops in a path. Min 1, max 8.", - }, - [this](int arg) { - if (arg < 1 or arg > 8) - throw std::invalid_argument("[endpoint]:hops must be >= 1 and <= 8"); - hops = arg; - }); - - conf.define_option( - "network", - "paths", - ClientOnly, - PathsDefault, - Comment{ - "Number of paths to maintain at any given time.", - }, - [this](int arg) { - if (arg < 3 or arg > 8) - throw std::invalid_argument("[endpoint]:paths must be >= 3 and <= 8"); - paths = arg; - }); - - conf.define_option( - "network", - "exit", - ClientOnly, - Default{false}, - assignment_acceptor(allow_exit), - Comment{ - "Whether or not we should act as an exit node. Beware that this increases demand", - "on the server and may pose liability concerns. Enable at your own risk.", - }); - - conf.define_option( - "network", - "owned-range", - MultiValue, - Comment{ - "When in exit mode announce we allow a private range in our introset. For example:", - " owned-range=10.0.0.0/24", - }, - [this](std::string arg) { - IPRange range; - if (not range.FromString(arg)) - throw std::invalid_argument{"bad ip range: '" + arg + "'"}; - owned_ranges.insert(range); - }); - - conf.define_option( - "network", - "traffic-whitelist", - MultiValue, - Comment{ - "Adds an IP traffic type whitelist; can be specified multiple times. If any are", - "specified then only matched traffic will be allowed and all other traffic will be", - "dropped. Examples:", - " traffic-whitelist=tcp", - "would allow all TCP/IP packets (regardless of port);", - " traffic-whitelist=0x69", - "would allow IP traffic with IP protocol 0x69;", - " traffic-whitelist=udp/53", - "would allow UDP port 53; and", - " traffic-whitelist=tcp/smtp", - "would allow TCP traffic on the standard smtp port (21).", - }, - [this](std::string arg) { - if (not traffic_policy) - traffic_policy = net::TrafficPolicy{}; - - // this will throw on error - traffic_policy->protocols.emplace(arg); - }); - - conf.define_option( - "network", - "exit-node", - ClientOnly, - MultiValue, - Comment{ - "Specify a `.loki` address and an optional ip range to use as an exit broker.", - "Examples:", - " exit-node=whatever.loki", - "would map all exit traffic through whatever.loki; and", - " exit-node=stuff.loki:100.0.0.0/24", - "would map the IP range 100.0.0.0/24 through stuff.loki.", - "This option can be specified multiple times (to map different IP ranges).", - }, - [this](std::string arg) { - if (arg.empty()) - return; - service::Address exit; - IPRange range; - const auto pos = arg.find(":"); - if (pos == std::string::npos) - { - range.FromString("::/0"); - } - else if (not range.FromString(arg.substr(pos + 1))) - { - throw std::invalid_argument("[network]:exit-node invalid ip range for exit provided"); - } - if (pos != std::string::npos) - { - arg = arg.substr(0, pos); - } - - if (service::is_valid_name(arg)) - { - ons_exit_map.Insert(range, arg); - return; - } - - if (arg != "null" and not exit.FromString(arg)) - { - throw std::invalid_argument{fmt::format("[network]:exit-node bad address: {}", arg)}; - } - exit_map.Insert(range, exit); - }); - - conf.define_option( - "network", - "exit-auth", - ClientOnly, - MultiValue, - Comment{ - "Specify an optional authentication code required to use a non-public exit node.", - "For example:", - " exit-auth=myfavouriteexit.loki:abc", - "uses the authentication code `abc` whenever myfavouriteexit.loki is accessed.", - "Can be specified multiple times to store codes for different exit nodes.", - }, - [this](std::string arg) { - if (arg.empty()) - return; - service::Address exit; - service::AuthInfo auth; - const auto pos = arg.find(":"); - if (pos == std::string::npos) - { - throw std::invalid_argument( - "[network]:exit-auth invalid format, expects " - "exit-address.loki:auth-code-goes-here"); - } - const auto exit_str = arg.substr(0, pos); - auth.token = arg.substr(pos + 1); - - if (service::is_valid_name(exit_str)) - { - ons_exit_auths.emplace(exit_str, auth); - return; - } - - if (not exit.FromString(exit_str)) - { - throw std::invalid_argument("[network]:exit-auth invalid exit address"); - } - exit_auths.emplace(exit, auth); - }); - - conf.define_option( - "network", - "auto-routing", - ClientOnly, - Default{true}, - Comment{ - "Enable / disable automatic route configuration.", - "When this is enabled and an exit is used Lokinet will automatically configure the", - "operating system routes to route public internet traffic through the exit node.", - "This is enabled by default, but can be disabled if advanced/manual exit routing", - "configuration is desired."}, - assignment_acceptor(enable_route_poker)); - - conf.define_option( - "network", - "blackhole-routes", - ClientOnly, - Default{true}, - Comment{ - "Enable / disable route configuration blackholes.", - "When enabled lokinet will drop IPv4 and IPv6 traffic (when in exit mode) that is not", - "handled in the exit configuration. Enabled by default."}, - assignment_acceptor(blackhole_routes)); - - conf.define_option( - "network", - "ifname", - Comment{ - "Interface name for lokinet traffic. If unset lokinet will look for a free name", - "matching 'lokitunN', starting at N=0 (e.g. lokitun0, lokitun1, ...).", - }, - assignment_acceptor(if_name)); - - conf.define_option( - "network", - "ifaddr", - Comment{ - "Local IP and range for lokinet traffic. For example, 172.16.0.1/16 to use", - "172.16.0.1 for this machine and 172.16.x.y for remote peers. If omitted then", - "lokinet will attempt to find an unused private range.", - }, - [this](std::string arg) { - if (not if_addr.FromString(arg)) - { - throw std::invalid_argument{fmt::format("[network]:ifaddr invalid value: '{}'", arg)}; - } - }); - - conf.define_option( - "network", - "ip6-range", - ClientOnly, - Comment{ - "For all IPv6 exit traffic you will use this as the base address bitwised or'd with ", - "the v4 address in use.", - "To disable ipv6 set this to an empty value.", - "!!! WARNING !!! Disabling ipv6 tunneling when you have ipv6 routes WILL lead to ", - "de-anonymization as lokinet will no longer carry your ipv6 traffic.", - }, - IP6RangeDefault, - [this](std::string arg) { - if (arg.empty()) - { - LogError( - "!!! Disabling ipv6 tunneling when you have ipv6 routes WILL lead to " - "de-anonymization as lokinet will no longer carry your ipv6 traffic !!!"); - base_ipv6_addr = std::nullopt; - return; - } - base_ipv6_addr = huint128_t{}; - if (not base_ipv6_addr->FromString(arg)) - throw std::invalid_argument{ - fmt::format("[network]:ip6-range invalid value: '{}'", arg)}; - }); - - // TODO: could be useful for snodes in the future, but currently only implemented for clients: - conf.define_option( - "network", - "mapaddr", - ClientOnly, - MultiValue, - Comment{ - "Map a remote `.loki` address to always use a fixed local IP. For example:", - " mapaddr=whatever.loki:172.16.0.10", - "maps `whatever.loki` to `172.16.0.10` instead of using the next available IP.", - "The given IP address must be inside the range configured by ifaddr=", - }, - [this](std::string arg) { - if (arg.empty()) - return; - huint128_t ip; - service::Address addr; - const auto pos = arg.find(":"); - if (pos == std::string::npos) - { - throw std::invalid_argument{fmt::format("[endpoint]:mapaddr invalid entry: {}", arg)}; - } - std::string addrstr = arg.substr(0, pos); - std::string ipstr = arg.substr(pos + 1); - if (not ip.FromString(ipstr)) - { - huint32_t ipv4; - if (not ipv4.FromString(ipstr)) + struct ConfigGenParameters_impl : public ConfigGenParameters + { + const llarp::net::Platform* Net_ptr() const override { - throw std::invalid_argument{fmt::format("[endpoint]:mapaddr invalid ip: {}", ipstr)}; + return llarp::net::Platform::Default_ptr(); } - ip = net::ExpandV4(ipv4); - } - if (not addr.FromString(addrstr)) - { - throw std::invalid_argument{ - fmt::format("[endpoint]:mapaddr invalid addresss: {}", addrstr)}; - } - if (map_addrs.find(ip) != map_addrs.end()) - { - throw std::invalid_argument{ - fmt::format("[endpoint]:mapaddr ip already mapped: {}", ipstr)}; - } - map_addrs[ip] = addr; - }); - - conf.define_option( - "network", - "blacklist-snode", - ClientOnly, - MultiValue, - Comment{ - "Adds a lokinet relay `.snode` address to the list of relays to avoid when", - "building paths. Can be specified multiple times.", - }, - [this](std::string arg) { - RouterID id; - if (not id.from_string(arg)) - throw std::invalid_argument{fmt::format("Invalid RouterID: {}", arg)}; - - auto itr = snode_blacklist.emplace(std::move(id)); - if (not itr.second) - throw std::invalid_argument{fmt::format("Duplicate blacklist-snode: {}", arg)}; - }); + }; + } // namespace - // TODO: support SRV records for routers, but for now client only - conf.define_option( - "network", - "srv", - ClientOnly, - MultiValue, - Comment{ - "Specify SRV Records for services hosted on the SNApp for protocols that use SRV", - "records for service discovery. Each line specifies a single SRV record as:", - " srv=_service._protocol priority weight port target.loki", - "and can be specified multiple times as needed.", - "For more info see", - "https://docs.oxen.io/products-built-on-oxen/lokinet/snapps/hosting-snapps", - "and general description of DNS SRV record configuration.", - }, - [this](std::string arg) { - llarp::dns::SRVData newSRV; - if (not newSRV.fromString(arg)) - throw std::invalid_argument{fmt::format("Invalid SRV Record string: {}", arg)}; - - srv_records.push_back(std::move(newSRV)); - }); - - conf.define_option( - "network", - "path-alignment-timeout", - ClientOnly, - Comment{ - "How long to wait (in seconds) for a path to align to a pivot router when establishing", - "a path through the network to a remote .loki address.", - }, - [this](int val) { - if (val <= 0) + void RouterConfig::define_config_options( + ConfigDefinition& conf, const ConfigGenParameters& params) + { + constexpr Default DefaultJobQueueSize{1024 * 8}; + constexpr Default DefaultWorkerThreads{0}; + constexpr Default DefaultBlockBogons{true}; + + conf.define_option( + "router", "job-queue-size", DefaultJobQueueSize, Hidden, [this](int arg) { + if (arg < 1024) + throw std::invalid_argument("job-queue-size must be 1024 or greater"); + + job_que_size = arg; + }); + + conf.define_option( + "router", + "netid", + Default{llarp::LOKINET_DEFAULT_NETID}, + Comment{ + "Network ID; this is '"s + llarp::LOKINET_DEFAULT_NETID + "' for mainnet, '"s + + llarp::LOKINET_TESTNET_NETID + "' for testnet."s, + }, + [this](std::string arg) { + if (arg.size() > NETID_SIZE) + throw std::invalid_argument{ + fmt::format("netid is too long, max length is {}", NETID_SIZE)}; + + net_id = std::move(arg); + }); + + conf.define_option( + "router", + "relay-connections", + Default{CLIENT_ROUTER_CONNECTIONS}, + ClientOnly, + Comment{ + "Minimum number of routers lokinet client will attempt to maintain connections to.", + }, + [=](int arg) { + if (arg < CLIENT_ROUTER_CONNECTIONS) + throw std::invalid_argument{fmt::format( + "Client relay connections must be >= {}", CLIENT_ROUTER_CONNECTIONS)}; + + client_router_connections = arg; + }); + + conf.define_option( + "router", + "min-connections", + Deprecated, + Comment{ + "Minimum number of routers lokinet will attempt to maintain connections to.", + }, + [=](int) { + log::warning( + logcat, + "Router min-connections is deprecated; use relay-connections for clients"); + }); + + conf.define_option( + "router", + "max-connections", + Deprecated, + Comment{ + "Maximum number (hard limit) of routers lokinet will be connected to at any time.", + }, + [=](int) { + log::warning( + logcat, + "Router max-connections is deprecated; use relay-connections for clients"); + }); + + conf.define_option("router", "nickname", Deprecated); + + conf.define_option( + "router", + "data-dir", + Default{params.default_data_dir}, + Comment{ + "Optional directory for containing lokinet runtime data. This includes generated", + "private keys.", + }, + [this](fs::path arg) { + if (arg.empty()) + throw std::invalid_argument("[router]:data-dir is empty"); + if (not fs::exists(arg)) + throw std::runtime_error{ + fmt::format("Specified [router]:data-dir {} does not exist", arg)}; + + data_dir = std::move(arg); + }); + + conf.define_option( + "router", + "public-ip", + RelayOnly, + Comment{ + "For complex network configurations where the detected IP is incorrect or " + "non-public", + "this setting specifies the public IP at which this router is reachable. When", + "provided the public-port option must also be specified.", + }, + [this](std::string arg) { public_ip = std::move(arg); }); + + conf.define_option("router", "public-address", Hidden, [](std::string) { throw std::invalid_argument{ - "invalid path alignment timeout: " + std::to_string(val) + " <= 0"}; - path_alignment_timeout = std::chrono::seconds{val}; + "[router]:public-address option no longer supported, use [router]:public-ip and " + "[router]:public-port instead"}; }); - conf.define_option( - "network", - "persist-addrmap-file", - ClientOnly, - Default{fs::path{params.default_data_dir / "addrmap.dat"}}, - Comment{ - "If given this specifies a file in which to record mapped local tunnel addresses so", - "the same local address will be used for the same lokinet address on reboot. If this", - "is not specified then the local IP of remote lokinet targets will not persist across", - "restarts of lokinet.", - }, - [this](fs::path arg) { - if (arg.empty()) - throw std::invalid_argument("persist-addrmap-file cannot be empty"); - addr_map_persist_file = arg; - }); + conf.define_option( + "router", + "public-port", + RelayOnly, + Comment{ + "When specifying public-ip=, this specifies the public UDP port at which this " + "lokinet", + "router is reachable. Required when public-ip is used.", + }, + [this](uint16_t arg) { + if (arg <= 0 || arg > std::numeric_limits::max()) + throw std::invalid_argument("public-port must be >= 0 and <= 65536"); + public_port = arg; + }); + + conf.define_option( + "router", + "worker-threads", + DefaultWorkerThreads, + Comment{ + "The number of threads available for performing cryptographic functions.", + "The minimum is one thread, but network performance may increase with more.", + "threads. Should not exceed the number of logical CPU cores.", + "0 means use the number of logical CPU cores detected at startup.", + }, + [this](int arg) { + if (arg < 0) + throw std::invalid_argument("worker-threads must be >= 0"); + + worker_threads = arg; + }); + + // Hidden option because this isn't something that should ever be turned off occasionally + // when doing dev/testing work. + conf.define_option( + "router", + "block-bogons", + DefaultBlockBogons, + Hidden, + assignment_acceptor(block_bogons)); + + constexpr auto relative_to_datadir = + "An absolute path is used as-is, otherwise relative to 'data-dir'."; + + conf.define_option( + "router", + "contact-file", + RelayOnly, + Default{llarp::our_rc_filename}, + assignment_acceptor(rc_file), + Comment{ + "Filename in which to store the router contact file", + relative_to_datadir, + }); + + conf.define_option( + "router", + "encryption-privkey", + RelayOnly, + Default{llarp::our_enc_key_filename}, + assignment_acceptor(enckey_file), + Comment{ + "Filename in which to store the encryption private key", + relative_to_datadir, + }); + + conf.define_option( + "router", + "ident-privkey", + RelayOnly, + Default{llarp::our_identity_filename}, + assignment_acceptor(idkey_file), + Comment{ + "Filename in which to store the identity private key", + relative_to_datadir, + }); + + conf.define_option( + "router", + "transport-privkey", + RelayOnly, + Default{llarp::our_transport_key_filename}, + assignment_acceptor(transkey_file), + Comment{ + "Filename in which to store the transport private key.", + relative_to_datadir, + }); + + // Deprecated options: + + // these weren't even ever used! + conf.define_option("router", "max-routers", Deprecated); + conf.define_option("router", "min-routers", Deprecated); + + // TODO: this may have been a synonym for [router]worker-threads + conf.define_option("router", "threads", Deprecated); + conf.define_option("router", "net-threads", Deprecated); + + is_relay = params.is_relay; + } - // Deprecated options: - conf.define_option("network", "enabled", Deprecated); - } + void NetworkConfig::define_config_options( + ConfigDefinition& conf, const ConfigGenParameters& params) + { + (void)params; + + static constexpr Default ProfilingValueDefault{true}; + static constexpr Default SaveProfilesDefault{true}; + static constexpr Default ReachableDefault{true}; + static constexpr Default HopsDefault{4}; + static constexpr Default PathsDefault{6}; + static constexpr Default IP6RangeDefault{"fd00::"}; + + conf.define_option( + "network", "type", Default{"tun"}, Hidden, assignment_acceptor(endpoint_type)); + + conf.define_option( + "network", + "save-profiles", + SaveProfilesDefault, + Hidden, + assignment_acceptor(save_profiles)); + + conf.define_option( + "network", + "profiling", + ProfilingValueDefault, + Hidden, + assignment_acceptor(enable_profiling)); + + conf.define_option("network", "profiles", Deprecated); + + conf.define_option( + "network", + "strict-connect", + ClientOnly, + MultiValue, + [this](std::string value) { + RouterID router; + if (not router.from_string(value)) + throw std::invalid_argument{"bad snode value: " + value}; + if (not strict_connect.insert(router).second) + throw std::invalid_argument{"duplicate strict connect snode: " + value}; + }, + Comment{ + "Public keys of routers which will act as pinned first-hops. This may be used to", + "provide a trusted router (consider that you are not fully anonymous with your", + "first hop). This REQUIRES two or more nodes to be specified.", + }); + + conf.define_option( + "network", + "keyfile", + ClientOnly, + assignment_acceptor(keyfile), + Comment{ + "The private key to persist address with. If not specified the address will be", + "ephemeral.", + }); + + conf.define_option( + "network", + "auth", + ClientOnly, + Comment{ + "Set the endpoint authentication mechanism.", + "none/whitelist/lmq/file", + }, + [this](std::string arg) { + if (arg.empty()) + return; + auth_type = service::parse_auth_type(arg); + }); + + conf.define_option( + "network", + "auth-lmq", + ClientOnly, + assignment_acceptor(auth_url), + Comment{ + "lmq endpoint to talk to for authenticating new sessions", + "ipc:///var/lib/lokinet/auth.socket", + "tcp://127.0.0.1:5555", + }); + + conf.define_option( + "network", + "auth-lmq-method", + ClientOnly, + Default{"llarp.auth"}, + Comment{ + "lmq function to call for authenticating new sessions", + "llarp.auth", + }, + [this](std::string arg) { + if (arg.empty()) + return; + auth_method = std::move(arg); + }); + + conf.define_option( + "network", + "auth-whitelist", + ClientOnly, + MultiValue, + Comment{ + "manually add a remote endpoint by .loki address to the access whitelist", + }, + [this](std::string arg) { + service::Address addr; + if (not addr.FromString(arg)) + throw std::invalid_argument{fmt::format("bad loki address: {}", arg)}; + auth_whitelist.emplace(std::move(addr)); + }); + + conf.define_option( + "network", + "auth-file", + ClientOnly, + MultiValue, + Comment{ + "Read auth tokens from file to accept endpoint auth", + "Can be provided multiple times", + }, + [this](fs::path arg) { + if (not fs::exists(arg)) + throw std::invalid_argument{ + fmt::format("cannot load auth file {}: file does not exist", arg)}; + auth_files.emplace(std::move(arg)); + }); + conf.define_option( + "network", + "auth-file-type", + ClientOnly, + Comment{ + "How to interpret the contents of an auth file.", + "Possible values: hashes, plaintext", + }, + [this](std::string arg) { + auth_file_type = service::parse_auth_file_type(std::move(arg)); + }); + + conf.define_option( + "network", + "auth-static", + ClientOnly, + MultiValue, + Comment{ + "Manually add a static auth code to accept for endpoint auth", + "Can be provided multiple times", + }, + [this](std::string arg) { auth_static_tokens.emplace(std::move(arg)); }); + + conf.define_option( + "network", + "reachable", + ClientOnly, + ReachableDefault, + assignment_acceptor(is_reachable), + Comment{ + "Determines whether we will pubish our snapp's introset to the DHT.", + }); + + conf.define_option( + "network", + "hops", + HopsDefault, + Comment{ + "Number of hops in a path. Min 1, max 8.", + }, + [this](int arg) { + if (arg < 1 or arg > 8) + throw std::invalid_argument("[endpoint]:hops must be >= 1 and <= 8"); + hops = arg; + }); + + conf.define_option( + "network", + "paths", + ClientOnly, + PathsDefault, + Comment{ + "Number of paths to maintain at any given time.", + }, + [this](int arg) { + if (arg < 3 or arg > 8) + throw std::invalid_argument("[endpoint]:paths must be >= 3 and <= 8"); + paths = arg; + }); + + conf.define_option( + "network", + "exit", + ClientOnly, + Default{false}, + assignment_acceptor(allow_exit), + Comment{ + "Whether or not we should act as an exit node. Beware that this increases demand", + "on the server and may pose liability concerns. Enable at your own risk.", + }); + + conf.define_option( + "network", + "owned-range", + MultiValue, + Comment{ + "When in exit mode announce we allow a private range in our introset. For " + "example:", + " owned-range=10.0.0.0/24", + }, + [this](std::string arg) { + IPRange range; + if (not range.FromString(arg)) + throw std::invalid_argument{"bad ip range: '" + arg + "'"}; + owned_ranges.insert(range); + }); + + conf.define_option( + "network", + "traffic-whitelist", + MultiValue, + Comment{ + "Adds an IP traffic type whitelist; can be specified multiple times. If any are", + "specified then only matched traffic will be allowed and all other traffic will be", + "dropped. Examples:", + " traffic-whitelist=tcp", + "would allow all TCP/IP packets (regardless of port);", + " traffic-whitelist=0x69", + "would allow IP traffic with IP protocol 0x69;", + " traffic-whitelist=udp/53", + "would allow UDP port 53; and", + " traffic-whitelist=tcp/smtp", + "would allow TCP traffic on the standard smtp port (21).", + }, + [this](std::string arg) { + if (not traffic_policy) + traffic_policy = net::TrafficPolicy{}; + + // this will throw on error + traffic_policy->protocols.emplace(arg); + }); + + conf.define_option( + "network", + "exit-node", + ClientOnly, + MultiValue, + Comment{ + "Specify a `.loki` address and an optional ip range to use as an exit broker.", + "Examples:", + " exit-node=whatever.loki", + "would map all exit traffic through whatever.loki; and", + " exit-node=stuff.loki:100.0.0.0/24", + "would map the IP range 100.0.0.0/24 through stuff.loki.", + "This option can be specified multiple times (to map different IP ranges).", + }, + [this](std::string arg) { + if (arg.empty()) + return; + service::Address exit; + IPRange range; + const auto pos = arg.find(":"); + if (pos == std::string::npos) + { + range.FromString("::/0"); + } + else if (not range.FromString(arg.substr(pos + 1))) + { + throw std::invalid_argument( + "[network]:exit-node invalid ip range for exit provided"); + } + if (pos != std::string::npos) + { + arg = arg.substr(0, pos); + } + + if (service::is_valid_name(arg)) + { + ons_exit_map.Insert(range, arg); + return; + } + + if (arg != "null" and not exit.FromString(arg)) + { + throw std::invalid_argument{ + fmt::format("[network]:exit-node bad address: {}", arg)}; + } + exit_map.Insert(range, exit); + }); + + conf.define_option( + "network", + "exit-auth", + ClientOnly, + MultiValue, + Comment{ + "Specify an optional authentication code required to use a non-public exit node.", + "For example:", + " exit-auth=myfavouriteexit.loki:abc", + "uses the authentication code `abc` whenever myfavouriteexit.loki is accessed.", + "Can be specified multiple times to store codes for different exit nodes.", + }, + [this](std::string arg) { + if (arg.empty()) + return; + service::Address exit; + service::AuthInfo auth; + const auto pos = arg.find(":"); + if (pos == std::string::npos) + { + throw std::invalid_argument( + "[network]:exit-auth invalid format, expects " + "exit-address.loki:auth-code-goes-here"); + } + const auto exit_str = arg.substr(0, pos); + auth.token = arg.substr(pos + 1); + + if (service::is_valid_name(exit_str)) + { + ons_exit_auths.emplace(exit_str, auth); + return; + } + + if (not exit.FromString(exit_str)) + { + throw std::invalid_argument("[network]:exit-auth invalid exit address"); + } + exit_auths.emplace(exit, auth); + }); + + conf.define_option( + "network", + "auto-routing", + ClientOnly, + Default{true}, + Comment{ + "Enable / disable automatic route configuration.", + "When this is enabled and an exit is used Lokinet will automatically configure the", + "operating system routes to route public internet traffic through the exit node.", + "This is enabled by default, but can be disabled if advanced/manual exit routing", + "configuration is desired."}, + assignment_acceptor(enable_route_poker)); + + conf.define_option( + "network", + "blackhole-routes", + ClientOnly, + Default{true}, + Comment{ + "Enable / disable route configuration blackholes.", + "When enabled lokinet will drop IPv4 and IPv6 traffic (when in exit mode) that is " + "not", + "handled in the exit configuration. Enabled by default."}, + assignment_acceptor(blackhole_routes)); + + conf.define_option( + "network", + "ifname", + Comment{ + "Interface name for lokinet traffic. If unset lokinet will look for a free name", + "matching 'lokitunN', starting at N=0 (e.g. lokitun0, lokitun1, ...).", + }, + assignment_acceptor(if_name)); + + conf.define_option( + "network", + "ifaddr", + Comment{ + "Local IP and range for lokinet traffic. For example, 172.16.0.1/16 to use", + "172.16.0.1 for this machine and 172.16.x.y for remote peers. If omitted then", + "lokinet will attempt to find an unused private range.", + }, + [this](std::string arg) { + if (not if_addr.FromString(arg)) + { + throw std::invalid_argument{ + fmt::format("[network]:ifaddr invalid value: '{}'", arg)}; + } + }); + + conf.define_option( + "network", + "ip6-range", + ClientOnly, + Comment{ + "For all IPv6 exit traffic you will use this as the base address bitwised or'd " + "with ", + "the v4 address in use.", + "To disable ipv6 set this to an empty value.", + "!!! WARNING !!! Disabling ipv6 tunneling when you have ipv6 routes WILL lead to ", + "de-anonymization as lokinet will no longer carry your ipv6 traffic.", + }, + IP6RangeDefault, + [this](std::string arg) { + if (arg.empty()) + { + LogError( + "!!! Disabling ipv6 tunneling when you have ipv6 routes WILL lead to " + "de-anonymization as lokinet will no longer carry your ipv6 traffic !!!"); + base_ipv6_addr = std::nullopt; + return; + } + base_ipv6_addr = huint128_t{}; + if (not base_ipv6_addr->FromString(arg)) + throw std::invalid_argument{ + fmt::format("[network]:ip6-range invalid value: '{}'", arg)}; + }); + + // TODO: could be useful for snodes in the future, but currently only implemented for + // clients: + conf.define_option( + "network", + "mapaddr", + ClientOnly, + MultiValue, + Comment{ + "Map a remote `.loki` address to always use a fixed local IP. For example:", + " mapaddr=whatever.loki:172.16.0.10", + "maps `whatever.loki` to `172.16.0.10` instead of using the next available IP.", + "The given IP address must be inside the range configured by ifaddr=", + }, + [this](std::string arg) { + if (arg.empty()) + return; + huint128_t ip; + service::Address addr; + const auto pos = arg.find(":"); + if (pos == std::string::npos) + { + throw std::invalid_argument{ + fmt::format("[endpoint]:mapaddr invalid entry: {}", arg)}; + } + std::string addrstr = arg.substr(0, pos); + std::string ipstr = arg.substr(pos + 1); + if (not ip.FromString(ipstr)) + { + huint32_t ipv4; + if (not ipv4.FromString(ipstr)) + { + throw std::invalid_argument{ + fmt::format("[endpoint]:mapaddr invalid ip: {}", ipstr)}; + } + ip = net::ExpandV4(ipv4); + } + if (not addr.FromString(addrstr)) + { + throw std::invalid_argument{ + fmt::format("[endpoint]:mapaddr invalid addresss: {}", addrstr)}; + } + if (map_addrs.find(ip) != map_addrs.end()) + { + throw std::invalid_argument{ + fmt::format("[endpoint]:mapaddr ip already mapped: {}", ipstr)}; + } + map_addrs[ip] = addr; + }); + + conf.define_option( + "network", + "blacklist-snode", + ClientOnly, + MultiValue, + Comment{ + "Adds a lokinet relay `.snode` address to the list of relays to avoid when", + "building paths. Can be specified multiple times.", + }, + [this](std::string arg) { + RouterID id; + if (not id.from_string(arg)) + throw std::invalid_argument{fmt::format("Invalid RouterID: {}", arg)}; + + auto itr = snode_blacklist.emplace(std::move(id)); + if (not itr.second) + throw std::invalid_argument{fmt::format("Duplicate blacklist-snode: {}", arg)}; + }); + + // TODO: support SRV records for routers, but for now client only + conf.define_option( + "network", + "srv", + ClientOnly, + MultiValue, + Comment{ + "Specify SRV Records for services hosted on the SNApp for protocols that use SRV", + "records for service discovery. Each line specifies a single SRV record as:", + " srv=_service._protocol priority weight port target.loki", + "and can be specified multiple times as needed.", + "For more info see", + "https://docs.oxen.io/products-built-on-oxen/lokinet/snapps/hosting-snapps", + "and general description of DNS SRV record configuration.", + }, + [this](std::string arg) { + llarp::dns::SRVData newSRV; + if (not newSRV.fromString(arg)) + throw std::invalid_argument{fmt::format("Invalid SRV Record string: {}", arg)}; + + srv_records.push_back(std::move(newSRV)); + }); + + conf.define_option( + "network", + "path-alignment-timeout", + ClientOnly, + Comment{ + "How long to wait (in seconds) for a path to align to a pivot router when " + "establishing", + "a path through the network to a remote .loki address.", + }, + [this](int val) { + if (val <= 0) + throw std::invalid_argument{ + "invalid path alignment timeout: " + std::to_string(val) + " <= 0"}; + path_alignment_timeout = std::chrono::seconds{val}; + }); + + conf.define_option( + "network", + "persist-addrmap-file", + ClientOnly, + Default{fs::path{params.default_data_dir / "addrmap.dat"}}, + Comment{ + "If given this specifies a file in which to record mapped local tunnel addresses " + "so", + "the same local address will be used for the same lokinet address on reboot. If " + "this", + "is not specified then the local IP of remote lokinet targets will not persist " + "across", + "restarts of lokinet.", + }, + [this](fs::path arg) { + if (arg.empty()) + throw std::invalid_argument("persist-addrmap-file cannot be empty"); + addr_map_persist_file = arg; + }); + + // Deprecated options: + conf.define_option("network", "enabled", Deprecated); + } - void - DnsConfig::define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params) - { - (void)params; + void DnsConfig::define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params) + { + (void)params; - // Most non-linux platforms have loopback as 127.0.0.1/32, but linux uses 127.0.0.1/8 so that we - // can bind to other 127.* IPs to avoid conflicting with something else that may be listening on - // 127.0.0.1:53. - constexpr std::array DefaultDNSBind{ + // Most non-linux platforms have loopback as 127.0.0.1/32, but linux uses 127.0.0.1/8 so + // that we can bind to other 127.* IPs to avoid conflicting with something else that may be + // listening on 127.0.0.1:53. + constexpr std::array DefaultDNSBind{ #ifdef __linux__ #ifdef WITH_SYSTEMD - // when we have systemd support add a random high port on loopback as well - // see https://github.com/oxen-io/lokinet/issues/1887#issuecomment-1091897282 - Default{"127.0.0.1:0"}, + // when we have systemd support add a random high port on loopback as well + // see https://github.com/oxen-io/lokinet/issues/1887#issuecomment-1091897282 + Default{"127.0.0.1:0"}, #endif - Default{"127.3.2.1:53"}, + Default{"127.3.2.1:53"}, #else - Default{"127.0.0.1:53"}, + Default{"127.0.0.1:53"}, #endif - }; - - // Default, but if we get any upstream (including upstream=, i.e. empty string) we clear it - constexpr Default DefaultUpstreamDNS{"9.9.9.10:53"}; - upstream_dns.emplace_back(DefaultUpstreamDNS.val); - - conf.define_option( - "dns", - "upstream", - MultiValue, - Comment{ - "Upstream resolver(s) to use as fallback for non-loki addresses.", - "Multiple values accepted.", - }, - [=, first = true](std::string arg) mutable { - if (first) - { - upstream_dns.clear(); - first = false; - } - if (not arg.empty()) - { - auto& entry = upstream_dns.emplace_back(std::move(arg)); - if (not entry.getPort()) - entry.setPort(53); - } - }); - - conf.define_option( - "dns", - "l3-intercept", - Default{ - platform::is_windows or platform::is_android - or (platform::is_macos and not platform::is_apple_sysex)}, - Comment{"Intercept all dns traffic (udp/53) going into our lokinet network interface " - "instead of binding a local udp socket"}, - assignment_acceptor(raw)); - - conf.define_option( - "dns", - "query-bind", + }; + + // Default, but if we get any upstream (including upstream=, i.e. empty string) we clear it + constexpr Default DefaultUpstreamDNS{"9.9.9.10:53"}; + upstream_dns.emplace_back(DefaultUpstreamDNS.val); + + conf.define_option( + "dns", + "upstream", + MultiValue, + Comment{ + "Upstream resolver(s) to use as fallback for non-loki addresses.", + "Multiple values accepted.", + }, + [=, first = true](std::string arg) mutable { + if (first) + { + upstream_dns.clear(); + first = false; + } + if (not arg.empty()) + { + auto& entry = upstream_dns.emplace_back(std::move(arg)); + if (not entry.getPort()) + entry.setPort(53); + } + }); + + conf.define_option( + "dns", + "l3-intercept", + Default{ + platform::is_windows or platform::is_android + or (platform::is_macos and not platform::is_apple_sysex)}, + Comment{"Intercept all dns traffic (udp/53) going into our lokinet network interface " + "instead of binding a local udp socket"}, + assignment_acceptor(raw)); + + conf.define_option( + "dns", + "query-bind", #if defined(_WIN32) - Default{"0.0.0.0:0"}, + Default{"0.0.0.0:0"}, #else - Hidden, + Hidden, #endif - Comment{ - "Address to bind to for sending upstream DNS requests.", - }, - [this](std::string arg) { query_bind = SockAddr{arg}; }); - - conf.define_option( - "dns", - "bind", - DefaultDNSBind, - MultiValue, - Comment{ - "Address to bind to for handling DNS requests.", - }, - [=](std::string arg) { - SockAddr addr{arg}; - // set dns port if no explicit port specified - // explicit :0 allowed - if (not addr.getPort() and not ends_with(arg, ":0")) - addr.setPort(53); - bind_addr.emplace_back(addr); - }); - - conf.define_option( - "dns", - "add-hosts", - ClientOnly, - Comment{"Add a hosts file to the dns resolver", "For use with client side dns filtering"}, - [=](fs::path path) { - if (path.empty()) - return; - if (not fs::exists(path)) - throw std::invalid_argument{ - fmt::format("cannot add hosts file {} as it does not exist", path)}; - hostfiles.emplace_back(std::move(path)); - }); - - // Ignored option (used by the systemd service file to disable resolvconf configuration). - conf.define_option( - "dns", - "no-resolvconf", - ClientOnly, - Comment{ - "Can be uncommented and set to 1 to disable resolvconf configuration of lokinet DNS.", - "(This is not used directly by lokinet itself, but by the lokinet init scripts", - "on systems which use resolveconf)", - }); + Comment{ + "Address to bind to for sending upstream DNS requests.", + }, + [this](std::string arg) { query_bind = SockAddr{arg}; }); + + conf.define_option( + "dns", + "bind", + DefaultDNSBind, + MultiValue, + Comment{ + "Address to bind to for handling DNS requests.", + }, + [=](std::string arg) { + SockAddr addr{arg}; + // set dns port if no explicit port specified + // explicit :0 allowed + if (not addr.getPort() and not ends_with(arg, ":0")) + addr.setPort(53); + bind_addr.emplace_back(addr); + }); + + conf.define_option( + "dns", + "add-hosts", + ClientOnly, + Comment{ + "Add a hosts file to the dns resolver", "For use with client side dns filtering"}, + [=](fs::path path) { + if (path.empty()) + return; + if (not fs::exists(path)) + throw std::invalid_argument{ + fmt::format("cannot add hosts file {} as it does not exist", path)}; + hostfiles.emplace_back(std::move(path)); + }); + + // Ignored option (used by the systemd service file to disable resolvconf configuration). + conf.define_option( + "dns", + "no-resolvconf", + ClientOnly, + Comment{ + "Can be uncommented and set to 1 to disable resolvconf configuration of lokinet " + "DNS.", + "(This is not used directly by lokinet itself, but by the lokinet init scripts", + "on systems which use resolveconf)", + }); + + // forward the rest to libunbound + conf.add_undeclared_handler( + "dns", [this](auto, std::string_view key, std::string_view val) { + extra_opts.emplace(key, val); + }); + } - // forward the rest to libunbound - conf.add_undeclared_handler("dns", [this](auto, std::string_view key, std::string_view val) { - extra_opts.emplace(key, val); - }); - } - - void - LinksConfig::define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params) - { - conf.add_section_comments( - "bind", - { - "This section allows specifying the IPs that lokinet uses for incoming and outgoing", - "connections. For simple setups it can usually be left blank, but may be required", - "for routers with multiple IPs, or routers that must listen on a private IP with", - "forwarded public traffic. It can also be useful for clients that want to use a", - "consistent outgoing port for which firewall rules can be configured.", - }); + void LinksConfig::define_config_options( + ConfigDefinition& conf, const ConfigGenParameters& params) + { + conf.add_section_comments( + "bind", + { + "This section allows specifying the IPs that lokinet uses for incoming and " + "outgoing", + "connections. For simple setups it can usually be left blank, but may be required", + "for routers with multiple IPs, or routers that must listen on a private IP with", + "forwarded public traffic. It can also be useful for clients that want to use a", + "consistent outgoing port for which firewall rules can be configured.", + }); + + const auto* net_ptr = params.Net_ptr(); + + conf.define_option( + "bind", + "public-ip", + Hidden, + RelayOnly, + Comment{ + "The IP address to advertise to the network instead of the incoming= or " + "auto-detected", + "IP. This is typically required only when incoming= is used to listen on an " + "internal", + "private range IP address that received traffic forwarded from the public IP.", + }, + [this](std::string arg) { + public_addr = std::move(arg); + log::warning( + logcat, + "Using deprecated option; pass this value to [Router]:public-ip instead " + "PLEASE"); + }); + + conf.define_option( + "bind", + "public-port", + Hidden, + RelayOnly, + Comment{ + "The port to advertise to the network instead of the incoming= (or default) port.", + "This is typically required only when incoming= is used to listen on an internal", + "private range IP address/port that received traffic forwarded from the public IP.", + }, + [this](uint16_t arg) { + if (arg <= 0 || arg > std::numeric_limits::max()) + throw std::invalid_argument("public-port must be >= 0 and <= 65536"); + public_port = arg; + log::warning( + logcat, + "Using deprecated option; pass this value to [Router]:public-port instead " + "PLEASE"); + }); + + auto parse_addr_for_link = [net_ptr](const std::string& arg) { + std::optional maybe = std::nullopt; + std::string_view arg_v{arg}, host; + uint16_t p{DEFAULT_LISTEN_PORT}; + + if (auto pos = arg_v.find(':'); pos != arg_v.npos) + { + host = arg_v.substr(0, pos); - const auto* net_ptr = params.Net_ptr(); - - conf.define_option( - "bind", - "public-ip", - Hidden, - RelayOnly, - Comment{ - "The IP address to advertise to the network instead of the incoming= or auto-detected", - "IP. This is typically required only when incoming= is used to listen on an internal", - "private range IP address that received traffic forwarded from the public IP.", - }, - [this](std::string arg) { - public_addr = std::move(arg); - log::warning( - logcat, - "Using deprecated option; pass this value to [Router]:public-ip instead PLEASE"); - }); + if (not llarp::parse_int(arg_v.substr(pos + 1), p)) + throw std::invalid_argument{"Failed to parse port in arg:{}"_format(arg)}; + } - conf.define_option( - "bind", - "public-port", - Hidden, - RelayOnly, - Comment{ - "The port to advertise to the network instead of the incoming= (or default) port.", - "This is typically required only when incoming= is used to listen on an internal", - "private range IP address/port that received traffic forwarded from the public IP.", - }, - [this](uint16_t arg) { - if (arg <= 0 || arg > std::numeric_limits::max()) - throw std::invalid_argument("public-port must be >= 0 and <= 65536"); - public_port = arg; - log::warning( - logcat, - "Using deprecated option; pass this value to [Router]:public-port instead PLEASE"); - }); + if (host.empty()) + maybe = net_ptr->get_best_public_address(true, p); + else + maybe = oxen::quic::Address{std::string{host}, p}; + + if (maybe and maybe->is_loopback()) + throw std::invalid_argument{"{} is a loopback address"_format(arg)}; + + return maybe; + }; + + conf.define_option( + "bind", + "listen", + Comment{ + "IP and/or port for lokinet to bind to for inbound/outbound connections.", + "", + "If IP is omitted then lokinet will search for a local network interface with a", + "public IP address and use that IP (and will exit with an error if no such IP is " + "found", + "on the system). If port is omitted then lokinet defaults to 1090.", + "", + "Note: only one address will be accepted. If this option is not specified, it " + "will ", + "default", + "to the inbound or outbound value. Conversely, specifying this option will " + "supercede ", + "the", + "deprecated inbound/outbound opts.", + "", + "Examples:", + " listen=15.5.29.5:443", + " listen=10.0.2.2", + " listen=:1234", + "", + "Using a private range IP address (like the second example entry) will require " + "using", + "the public-ip= and public-port= to specify the public IP address at which this", + "router can be reached.", + }, + [this, parse_addr_for_link](const std::string& arg) { + if (auto a = parse_addr_for_link(arg); a and a->is_addressable()) + { + listen_addr = *a; + using_user_value = true; + using_new_api = true; + } + else + throw std::invalid_argument{"Could not parse listen address!"}; + }); + + conf.define_option( + "bind", + "inbound", + RelayOnly, + MultiValue, + Hidden, + [this, parse_addr_for_link](const std::string& arg) { + if (using_new_api) + throw std::runtime_error{ + "USE THE NEW API -- SPECIFY LOCAL ADDRESS UNDER [LISTEN]"}; + + if (auto a = parse_addr_for_link(arg); a and a->is_addressable()) + { + log::warning( + logcat, + "Loaded address from deprecated [inbound] options; update your config to " + "use " + "[bind]:listen instead PLEASE"); + listen_addr = *a; + using_user_value = true; + } + }); + + conf.define_option("bind", "outbound", MultiValue, Deprecated, Hidden); + + conf.add_undeclared_handler( + "bind", [this](std::string_view, std::string_view key, std::string_view val) { + if (using_new_api) + throw std::runtime_error{ + "USE THE NEW API -- SPECIFY LOCAL ADDRESS UNDER [LISTEN]"}; + + log::warning(logcat, "Please update your config to use [bind]:listen instead"); + + uint16_t port{0}; + + if (auto rv = llarp::parse_int(val, port); not rv) + throw std::runtime_error{ + "Could not parse port; stop using this deprecated handler"}; + + port = port == 0 ? DEFAULT_LISTEN_PORT : port; + + // special case: wildcard for outbound + if (key == "*") + { + log::warning( + logcat, + "Wildcat address referencing port {} is referencing deprecated outbound " + "config " + "options; use [bind]:listen instead", + port); + return; + } + + oxen::quic::Address temp; + + try + { + temp = oxen::quic::Address{std::string{key}, port}; + } + catch (const std::exception& e) + { + throw std::runtime_error{fmt::format( + "Could not parse address {}; please update your config to use " + "[bind]:listen " + "instead: {}", + key, + e.what())}; + } + + if (not temp.is_addressable()) + { + throw std::runtime_error{fmt::format( + "Invalid address: {}; stop using this deprecated handler, update your " + "config to " + "use " + "[bind]:listen instead PLEASE", + temp)}; + } + + listen_addr = std::move(temp); + using_user_value = true; + }); + } - auto parse_addr_for_link = [net_ptr](const std::string& arg) { - std::optional maybe = std::nullopt; - std::string_view arg_v{arg}, host; - uint16_t p{DEFAULT_LISTEN_PORT}; - - if (auto pos = arg_v.find(':'); pos != arg_v.npos) - { - host = arg_v.substr(0, pos); - - if (not llarp::parse_int(arg_v.substr(pos + 1), p)) - throw std::invalid_argument{"Failed to parse port in arg:{}"_format(arg)}; - } - - if (host.empty()) - maybe = net_ptr->get_best_public_address(true, p); - else - maybe = oxen::quic::Address{std::string{host}, p}; - - if (maybe and maybe->is_loopback()) - throw std::invalid_argument{"{} is a loopback address"_format(arg)}; - - return maybe; - }; - - conf.define_option( - "bind", - "listen", - Comment{ - "IP and/or port for lokinet to bind to for inbound/outbound connections.", - "", - "If IP is omitted then lokinet will search for a local network interface with a", - "public IP address and use that IP (and will exit with an error if no such IP is found", - "on the system). If port is omitted then lokinet defaults to 1090.", - "", - "Note: only one address will be accepted. If this option is not specified, it will ", - "default", - "to the inbound or outbound value. Conversely, specifying this option will supercede ", - "the", - "deprecated inbound/outbound opts.", - "", - "Examples:", - " listen=15.5.29.5:443", - " listen=10.0.2.2", - " listen=:1234", - "", - "Using a private range IP address (like the second example entry) will require using", - "the public-ip= and public-port= to specify the public IP address at which this", - "router can be reached.", - }, - [this, parse_addr_for_link](const std::string& arg) { - if (auto a = parse_addr_for_link(arg); a and a->is_addressable()) - { - listen_addr = *a; - using_user_value = true; - using_new_api = true; - } - else - throw std::invalid_argument{"Could not parse listen address!"}; - }); + void ApiConfig::define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params) + { + constexpr std::array DefaultRPCBind{ + Default{"tcp://127.0.0.1:1190"}, +#ifndef _WIN32 + Default{"ipc://rpc.sock"}, +#endif + }; + + conf.define_option( + "api", + "enabled", + Default{not params.is_relay}, + assignment_acceptor(enable_rpc_server), + Comment{ + "Determines whether or not the LMQ JSON API is enabled. Defaults ", + }); + + conf.define_option( + "api", + "bind", + DefaultRPCBind, + MultiValue, + [this, first = true](std::string arg) mutable { + if (first) + { + rpc_bind_addrs.clear(); + first = false; + } + if (arg.find("://") == std::string::npos) + { + arg = "tcp://" + arg; + } + rpc_bind_addrs.emplace_back(arg); + }, + Comment{ + "IP addresses and ports to bind to.", + "Recommend localhost-only for security purposes.", + }); + + conf.define_option("api", "authkey", Deprecated); + + // TODO: this was from pre-refactor: + // TODO: add pubkey to whitelist + } - conf.define_option( - "bind", - "inbound", - RelayOnly, - MultiValue, - Hidden, - [this, parse_addr_for_link](const std::string& arg) { - if (using_new_api) - throw std::runtime_error{"USE THE NEW API -- SPECIFY LOCAL ADDRESS UNDER [LISTEN]"}; - - if (auto a = parse_addr_for_link(arg); a and a->is_addressable()) - { - log::warning( - logcat, - "Loaded address from deprecated [inbound] options; update your config to use " - "[bind]:listen instead PLEASE"); - listen_addr = *a; - using_user_value = true; - } + void LokidConfig::define_config_options( + ConfigDefinition& conf, const ConfigGenParameters& params) + { + (void)params; + conf.define_option( + "lokid", + "disable-testing", + Default{false}, + Hidden, + RelayOnly, + Comment{ + "Development option: set to true to disable reachability testing when using ", + "testnet"}, + assignment_acceptor(disable_testing)); + + conf.define_option( + "lokid", + "rpc", + RelayOnly, + Required, + Comment{ + "oxenmq control address for for communicating with oxend. Depends on oxend's", + "lmq-local-control configuration option. By default this value should be", + "ipc://OXEND-DATA-DIRECTORY/oxend.sock, such as:", + " rpc=ipc:///var/lib/oxen/oxend.sock", + " rpc=ipc:///home/USER/.oxen/oxend.sock", + "but can use (non-default) TCP if oxend is configured that way:", + " rpc=tcp://127.0.0.1:5678", + }, + [this](std::string arg) { rpc_addr = oxenmq::address(arg); }); + + // Deprecated options: + conf.define_option("lokid", "jsonrpc", RelayOnly, Hidden, [](std::string arg) { + if (arg.empty()) + return; + throw std::invalid_argument( + "the [lokid]:jsonrpc option is no longer supported; please use the [lokid]:rpc " + "config " + "option instead with oxend's lmq-local-control address -- typically a value such " + "as " + "rpc=ipc:///var/lib/oxen/oxend.sock or rpc=ipc:///home/snode/.oxen/oxend.sock"); }); + conf.define_option("lokid", "enabled", RelayOnly, Deprecated); + conf.define_option("lokid", "username", Deprecated); + conf.define_option("lokid", "password", Deprecated); + conf.define_option("lokid", "service-node-seed", Deprecated); + } - conf.define_option("bind", "outbound", MultiValue, Deprecated, Hidden); - - conf.add_undeclared_handler( - "bind", [this](std::string_view, std::string_view key, std::string_view val) { - if (using_new_api) - throw std::runtime_error{"USE THE NEW API -- SPECIFY LOCAL ADDRESS UNDER [LISTEN]"}; - - log::warning(logcat, "Please update your config to use [bind]:listen instead"); - - uint16_t port{0}; - - if (auto rv = llarp::parse_int(val, port); not rv) - throw std::runtime_error{"Could not parse port; stop using this deprecated handler"}; + void BootstrapConfig::define_config_options( + ConfigDefinition& conf, const ConfigGenParameters& params) + { + (void)params; + + conf.define_option( + "bootstrap", + "seed-node", + Default{false}, + Comment{"Whether or not to run as a seed node. We will not have any bootstrap routers " + "configured."}, + assignment_acceptor(seednode)); + + conf.define_option( + "bootstrap", + "add-node", + MultiValue, + Comment{ + "Specify a bootstrap file containing a list of signed RouterContacts of service " + "nodes", + "which can act as a bootstrap. Can be specified multiple times.", + }, + [this](std::string arg) { + if (arg.empty()) + throw std::invalid_argument("cannot use empty filename as bootstrap"); + + files.emplace_back(std::move(arg)); + + if (not fs::exists(files.back())) + throw std::invalid_argument("file does not exist: " + arg); + }); + } - port = port == 0 ? DEFAULT_LISTEN_PORT : port; + void LoggingConfig::define_config_options( + ConfigDefinition& conf, const ConfigGenParameters& params) + { + (void)params; + + constexpr Default DefaultLogType{ + platform::is_android or platform::is_apple ? "system" : "print"}; + constexpr Default DefaultLogFile{""}; + + const Default DefaultLogLevel{params.is_relay ? "warn" : "info"}; + + conf.define_option( + "logging", + "type", + DefaultLogType, + [this](std::string arg) { type = log::type_from_string(arg); }, + Comment{ + "Log type (format). Valid options are:", + " print - print logs to standard output", + " system - logs directed to the system logger (syslog/eventlog/etc.)", + " file - plaintext formatting to a file", + }); + + conf.define_option( + "logging", + "level", + DefaultLogLevel, + [this](std::string arg) { level = log::level_from_string(arg); }, + Comment{ + "Minimum log level to print. Logging below this level will be ignored.", + "Valid log levels, in ascending order, are:", + " trace", + " debug", + " info", + " warn", + " error", + " critical", + " none", + }); + + conf.define_option( + "logging", + "file", + DefaultLogFile, + assignment_acceptor(file), + Comment{ + "When using type=file this is the output filename.", + }); + } - // special case: wildcard for outbound - if (key == "*") - { - log::warning( - logcat, - "Wildcat address referencing port {} is referencing deprecated outbound config " - "options; use [bind]:listen instead", - port); - return; - } - - oxen::quic::Address temp; - - try - { - temp = oxen::quic::Address{std::string{key}, port}; - } - catch (const std::exception& e) - { - throw std::runtime_error{fmt::format( - "Could not parse address {}; please update your config to use [bind]:listen " - "instead: {}", - key, - e.what())}; - } - - if (not temp.is_addressable()) - { - throw std::runtime_error{fmt::format( - "Invalid address: {}; stop using this deprecated handler, update your config to " - "use " - "[bind]:listen instead PLEASE", - temp)}; - } - - listen_addr = std::move(temp); - using_user_value = true; - }); - } + void PeerSelectionConfig::define_config_options( + ConfigDefinition& conf, const ConfigGenParameters& params) + { + (void)params; + + constexpr Default DefaultUniqueCIDR{32}; + conf.define_option( + "paths", + "unique-range-size", + DefaultUniqueCIDR, + ClientOnly, + [=](int arg) { + if (arg == 0) + { + unique_hop_netmask = arg; + } + else if (arg > 32 or arg < 4) + { + throw std::invalid_argument{ + "[paths]:unique-range-size must be between 4 and 32"}; + } + unique_hop_netmask = arg; + }, + Comment{ + "Netmask for router path selection; each router must be from a distinct IPv4 " + "subnet", + "of the given size.", + "E.g. 16 ensures that all routers are using IPs from distinct /16 IP ranges."}); - void - ApiConfig::define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params) - { - constexpr std::array DefaultRPCBind{ - Default{"tcp://127.0.0.1:1190"}, -#ifndef _WIN32 - Default{"ipc://rpc.sock"}, +#ifdef WITH_GEOIP + conf.defineOption( + "paths", + "exclude-country", + ClientOnly, + MultiValue, + [=](std::string arg) { + m_ExcludeCountries.emplace(lowercase_ascii_string(std::move(arg))); + }, + Comment{ + "Exclude a country given its 2 letter country code from being used in path builds.", + "For example:", + " exclude-country=DE", + "would avoid building paths through routers with IPs in Germany.", + "This option can be specified multiple times to exclude multiple countries"}); #endif - }; - - conf.define_option( - "api", - "enabled", - Default{not params.is_relay}, - assignment_acceptor(enable_rpc_server), - Comment{ - "Determines whether or not the LMQ JSON API is enabled. Defaults ", - }); + } - conf.define_option( - "api", - "bind", - DefaultRPCBind, - MultiValue, - [this, first = true](std::string arg) mutable { - if (first) - { - rpc_bind_addrs.clear(); - first = false; - } - if (arg.find("://") == std::string::npos) - { - arg = "tcp://" + arg; - } - rpc_bind_addrs.emplace_back(arg); - }, - Comment{ - "IP addresses and ports to bind to.", - "Recommend localhost-only for security purposes.", - }); + bool PeerSelectionConfig::check_rcs(const std::set& rcs) const + { + if (unique_hop_netmask == 0) + return true; + const auto netmask = netmask_ipv6_bits(96 + unique_hop_netmask); + std::set seenRanges; + for (const auto& hop : rcs) + { + const auto network_addr = net::In6ToHUInt(hop.addr6()->in6().sin6_addr) & netmask; + if (auto [it, inserted] = seenRanges.emplace(network_addr, netmask); not inserted) + { + return false; + } + } + return true; + } - conf.define_option("api", "authkey", Deprecated); - - // TODO: this was from pre-refactor: - // TODO: add pubkey to whitelist - } - - void - LokidConfig::define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params) - { - (void)params; - conf.define_option( - "lokid", - "disable-testing", - Default{false}, - Hidden, - RelayOnly, - Comment{ - "Development option: set to true to disable reachability testing when using ", - "testnet"}, - assignment_acceptor(disable_testing)); - - conf.define_option( - "lokid", - "rpc", - RelayOnly, - Required, - Comment{ - "oxenmq control address for for communicating with oxend. Depends on oxend's", - "lmq-local-control configuration option. By default this value should be", - "ipc://OXEND-DATA-DIRECTORY/oxend.sock, such as:", - " rpc=ipc:///var/lib/oxen/oxend.sock", - " rpc=ipc:///home/USER/.oxen/oxend.sock", - "but can use (non-default) TCP if oxend is configured that way:", - " rpc=tcp://127.0.0.1:5678", - }, - [this](std::string arg) { rpc_addr = oxenmq::address(arg); }); - - // Deprecated options: - conf.define_option("lokid", "jsonrpc", RelayOnly, Hidden, [](std::string arg) { - if (arg.empty()) - return; - throw std::invalid_argument( - "the [lokid]:jsonrpc option is no longer supported; please use the [lokid]:rpc config " - "option instead with oxend's lmq-local-control address -- typically a value such as " - "rpc=ipc:///var/lib/oxen/oxend.sock or rpc=ipc:///home/snode/.oxen/oxend.sock"); - }); - conf.define_option("lokid", "enabled", RelayOnly, Deprecated); - conf.define_option("lokid", "username", Deprecated); - conf.define_option("lokid", "password", Deprecated); - conf.define_option("lokid", "service-node-seed", Deprecated); - } - - void - BootstrapConfig::define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params) - { - (void)params; - - conf.define_option( - "bootstrap", - "seed-node", - Default{false}, - Comment{"Whether or not to run as a seed node. We will not have any bootstrap routers " - "configured."}, - assignment_acceptor(seednode)); - - conf.define_option( - "bootstrap", - "add-node", - MultiValue, - Comment{ - "Specify a bootstrap file containing a list of signed RouterContacts of service nodes", - "which can act as a bootstrap. Can be specified multiple times.", - }, - [this](std::string arg) { - if (arg.empty()) - throw std::invalid_argument("cannot use empty filename as bootstrap"); - - files.emplace_back(std::move(arg)); - - if (not fs::exists(files.back())) - throw std::invalid_argument("file does not exist: " + arg); - }); - } - - void - LoggingConfig::define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params) - { - (void)params; - - constexpr Default DefaultLogType{ - platform::is_android or platform::is_apple ? "system" : "print"}; - constexpr Default DefaultLogFile{""}; - - const Default DefaultLogLevel{params.is_relay ? "warn" : "info"}; - - conf.define_option( - "logging", - "type", - DefaultLogType, - [this](std::string arg) { type = log::type_from_string(arg); }, - Comment{ - "Log type (format). Valid options are:", - " print - print logs to standard output", - " system - logs directed to the system logger (syslog/eventlog/etc.)", - " file - plaintext formatting to a file", - }); + std::unique_ptr Config::make_gen_params() const + { + return std::make_unique(); + } - conf.define_option( - "logging", - "level", - DefaultLogLevel, - [this](std::string arg) { level = log::level_from_string(arg); }, - Comment{ - "Minimum log level to print. Logging below this level will be ignored.", - "Valid log levels, in ascending order, are:", - " trace", - " debug", - " info", - " warn", - " error", - " critical", - " none", - }); + Config::Config(std::optional datadir) + : data_dir{datadir ? std::move(*datadir) : fs::current_path()} + {} - conf.define_option( - "logging", - "file", - DefaultLogFile, - assignment_acceptor(file), - Comment{ - "When using type=file this is the output filename.", - }); - } - - void - PeerSelectionConfig::define_config_options( - ConfigDefinition& conf, const ConfigGenParameters& params) - { - (void)params; - - constexpr Default DefaultUniqueCIDR{32}; - conf.define_option( - "paths", - "unique-range-size", - DefaultUniqueCIDR, - ClientOnly, - [=](int arg) { - if (arg == 0) - { - unique_hop_netmask = arg; - } - else if (arg > 32 or arg < 4) - { - throw std::invalid_argument{"[paths]:unique-range-size must be between 4 and 32"}; - } - unique_hop_netmask = arg; - }, - Comment{ - "Netmask for router path selection; each router must be from a distinct IPv4 subnet", - "of the given size.", - "E.g. 16 ensures that all routers are using IPs from distinct /16 IP ranges."}); + constexpr auto GetOverridesDir = [](auto datadir) -> fs::path { return datadir / "conf.d"; }; -#ifdef WITH_GEOIP - conf.defineOption( - "paths", - "exclude-country", - ClientOnly, - MultiValue, - [=](std::string arg) { - m_ExcludeCountries.emplace(lowercase_ascii_string(std::move(arg))); - }, - Comment{ - "Exclude a country given its 2 letter country code from being used in path builds.", - "For example:", - " exclude-country=DE", - "would avoid building paths through routers with IPs in Germany.", - "This option can be specified multiple times to exclude multiple countries"}); -#endif - } - - bool - PeerSelectionConfig::check_rcs(const std::set& rcs) const - { - if (unique_hop_netmask == 0) - return true; - const auto netmask = netmask_ipv6_bits(96 + unique_hop_netmask); - std::set seenRanges; - for (const auto& hop : rcs) + void Config::save() { - const auto network_addr = net::In6ToHUInt(hop.addr6()->in6().sin6_addr) & netmask; - if (auto [it, inserted] = seenRanges.emplace(network_addr, netmask); not inserted) - { - return false; - } + const auto overridesDir = GetOverridesDir(data_dir); + if (not fs::exists(overridesDir)) + fs::create_directory(overridesDir); + parser.save(); } - return true; - } - - std::unique_ptr - Config::make_gen_params() const - { - return std::make_unique(); - } - - Config::Config(std::optional datadir) - : data_dir{datadir ? std::move(*datadir) : fs::current_path()} - {} - - constexpr auto GetOverridesDir = [](auto datadir) -> fs::path { return datadir / "conf.d"; }; - - void - Config::save() - { - const auto overridesDir = GetOverridesDir(data_dir); - if (not fs::exists(overridesDir)) - fs::create_directory(overridesDir); - parser.save(); - } - - void - Config::override(std::string section, std::string key, std::string value) - { - parser.add_override(GetOverridesDir(data_dir) / "overrides.ini", section, key, value); - } - - void - Config::load_overrides(ConfigDefinition& conf) const - { - ConfigParser parser; - const auto overridesDir = GetOverridesDir(data_dir); - if (fs::exists(overridesDir)) + + void Config::override(std::string section, std::string key, std::string value) { - for (const auto& f : fs::directory_iterator{overridesDir}) - { - if (not f.is_regular_file() or f.path().extension() != ".ini") - continue; - ConfigParser parser; - if (not parser.load_file(f.path())) - throw std::runtime_error{"cannot load '" + f.path().u8string() + "'"}; + parser.add_override(GetOverridesDir(data_dir) / "overrides.ini", section, key, value); + } - parser.iter_all_sections([&](std::string_view section, const SectionValues& values) { - for (const auto& [k, v] : values) - conf.add_config_value(section, k, v); - }); - } + void Config::load_overrides(ConfigDefinition& conf) const + { + ConfigParser parser; + const auto overridesDir = GetOverridesDir(data_dir); + if (fs::exists(overridesDir)) + { + for (const auto& f : fs::directory_iterator{overridesDir}) + { + if (not f.is_regular_file() or f.path().extension() != ".ini") + continue; + ConfigParser parser; + if (not parser.load_file(f.path())) + throw std::runtime_error{"cannot load '" + f.path().u8string() + "'"}; + + parser.iter_all_sections( + [&](std::string_view section, const SectionValues& values) { + for (const auto& [k, v] : values) + conf.add_config_value(section, k, v); + }); + } + } } - } - - void - Config::add_default(std::string section, std::string key, std::string val) - { - additional.emplace_back(std::array{section, key, val}); - } - - bool - Config::load_config_data(std::string_view ini, std::optional filename, bool isRelay) - { - auto params = make_gen_params(); - params->is_relay = isRelay; - params->default_data_dir = data_dir; - ConfigDefinition conf{isRelay}; - add_backcompat_opts(conf); - init_config(conf, *params); - - for (const auto& item : additional) + + void Config::add_default(std::string section, std::string key, std::string val) { - conf.add_config_value(item[0], item[1], item[2]); + additional.emplace_back(std::array{section, key, val}); } - parser.clear(); + bool Config::load_config_data( + std::string_view ini, std::optional filename, bool isRelay) + { + auto params = make_gen_params(); + params->is_relay = isRelay; + params->default_data_dir = data_dir; + ConfigDefinition conf{isRelay}; + add_backcompat_opts(conf); + init_config(conf, *params); + + for (const auto& item : additional) + { + conf.add_config_value(item[0], item[1], item[2]); + } - if (filename) - parser.set_filename(*filename); - else - parser.set_filename(fs::path{}); + parser.clear(); - if (not parser.load_from_str(ini)) - return false; + if (filename) + parser.set_filename(*filename); + else + parser.set_filename(fs::path{}); - parser.iter_all_sections([&](std::string_view section, const SectionValues& values) { - for (const auto& pair : values) - { - conf.add_config_value(section, pair.first, pair.second); - } - }); + if (not parser.load_from_str(ini)) + return false; - load_overrides(conf); + parser.iter_all_sections([&](std::string_view section, const SectionValues& values) { + for (const auto& pair : values) + { + conf.add_config_value(section, pair.first, pair.second); + } + }); - conf.process(); + load_overrides(conf); - return true; - } + conf.process(); - bool - Config::load(std::optional fname, bool isRelay) - { - std::string ini; - if (fname) - { - try - { - ini = util::file_to_string(*fname); - } - catch (const std::exception&) - { - return false; - } + return true; } - return load_config_data(ini, fname, isRelay); - } - - bool - Config::load_string(std::string_view ini, bool isRelay) - { - return load_config_data(ini, std::nullopt, isRelay); - } - - bool - Config::load_default_config(bool isRelay) - { - return load_string("", isRelay); - } - - void - Config::init_config(ConfigDefinition& conf, const ConfigGenParameters& params) - { - router.define_config_options(conf, params); - network.define_config_options(conf, params); - paths.define_config_options(conf, params); - dns.define_config_options(conf, params); - links.define_config_options(conf, params); - api.define_config_options(conf, params); - lokid.define_config_options(conf, params); - bootstrap.define_config_options(conf, params); - logging.define_config_options(conf, params); - } - - void - Config::add_backcompat_opts(ConfigDefinition& conf) - { - // These config sections don't exist anymore: - - conf.define_option("system", "user", Deprecated); - conf.define_option("system", "group", Deprecated); - conf.define_option("system", "pidfile", Deprecated); - - conf.define_option("netdb", "dir", Deprecated); - - conf.define_option("metrics", "json-metrics-path", Deprecated); - } - - void - ensure_config(fs::path dataDir, fs::path confFile, bool overwrite, bool asRouter) - { - // fail to overwrite if not instructed to do so - if (fs::exists(confFile) && !overwrite) + + bool Config::load(std::optional fname, bool isRelay) { - LogDebug("Not creating config file; it already exists."); - return; + std::string ini; + if (fname) + { + try + { + ini = util::file_to_string(*fname); + } + catch (const std::exception&) + { + return false; + } + } + return load_config_data(ini, fname, isRelay); } - const auto parent = confFile.parent_path(); - - // create parent dir if it doesn't exist - if ((not parent.empty()) and (not fs::exists(parent))) + bool Config::load_string(std::string_view ini, bool isRelay) { - fs::create_directory(parent); + return load_config_data(ini, std::nullopt, isRelay); } - llarp::LogInfo( - "Attempting to create config file for ", - (asRouter ? "router" : "client"), - " at ", - confFile); - - llarp::Config config{dataDir}; - std::string confStr; - if (asRouter) - confStr = config.generate_router_config_base(); - else - confStr = config.generate_client_config_base(); - - // open a filestream - try + bool Config::load_default_config(bool isRelay) { - util::buffer_to_file(confFile, confStr); + return load_string("", isRelay); } - catch (const std::exception& e) + + void Config::init_config(ConfigDefinition& conf, const ConfigGenParameters& params) { - throw std::runtime_error{ - fmt::format("Failed to write config data to {}: {}", confFile, e.what())}; + router.define_config_options(conf, params); + network.define_config_options(conf, params); + paths.define_config_options(conf, params); + dns.define_config_options(conf, params); + links.define_config_options(conf, params); + api.define_config_options(conf, params); + lokid.define_config_options(conf, params); + bootstrap.define_config_options(conf, params); + logging.define_config_options(conf, params); } - llarp::LogInfo("Generated new config ", confFile); - } + void Config::add_backcompat_opts(ConfigDefinition& conf) + { + // These config sections don't exist anymore: - void - generate_common_config_comments(ConfigDefinition& def) - { - // router - def.add_section_comments( - "router", - { - "Configuration for routing activity.", - }); + conf.define_option("system", "user", Deprecated); + conf.define_option("system", "group", Deprecated); + conf.define_option("system", "pidfile", Deprecated); - // logging - def.add_section_comments( - "logging", - { - "logging settings", - }); + conf.define_option("netdb", "dir", Deprecated); - // api - def.add_section_comments( - "api", - { - "JSON API settings", - }); + conf.define_option("metrics", "json-metrics-path", Deprecated); + } - // dns - def.add_section_comments( - "dns", + void ensure_config(fs::path dataDir, fs::path confFile, bool overwrite, bool asRouter) + { + // fail to overwrite if not instructed to do so + if (fs::exists(confFile) && !overwrite) { - "DNS configuration", - }); + LogDebug("Not creating config file; it already exists."); + return; + } - // bootstrap - def.add_section_comments( - "bootstrap", - { - "Configure nodes that will bootstrap us onto the network", - }); + const auto parent = confFile.parent_path(); - // network - def.add_section_comments( - "network", + // create parent dir if it doesn't exist + if ((not parent.empty()) and (not fs::exists(parent))) { - "Network settings", - }); - } - - std::string - Config::generate_client_config_base() - { - auto params = make_gen_params(); - params->is_relay = false; - params->default_data_dir = data_dir; - - llarp::ConfigDefinition def{false}; - init_config(def, *params); - generate_common_config_comments(def); - def.add_section_comments( - "paths", + fs::create_directory(parent); + } + + llarp::LogInfo( + "Attempting to create config file for ", + (asRouter ? "router" : "client"), + " at ", + confFile); + + llarp::Config config{dataDir}; + std::string confStr; + if (asRouter) + confStr = config.generate_router_config_base(); + else + confStr = config.generate_client_config_base(); + + // open a filestream + try { - "path selection algorithm options", - }); - - def.add_section_comments( - "network", + util::buffer_to_file(confFile, confStr); + } + catch (const std::exception& e) { - "Snapp settings", - }); + throw std::runtime_error{ + fmt::format("Failed to write config data to {}: {}", confFile, e.what())}; + } - return def.generate_ini_config(true); - } + llarp::LogInfo("Generated new config ", confFile); + } - std::string - Config::generate_router_config_base() - { - auto params = make_gen_params(); - params->is_relay = true; - params->default_data_dir = data_dir; + void generate_common_config_comments(ConfigDefinition& def) + { + // router + def.add_section_comments( + "router", + { + "Configuration for routing activity.", + }); - llarp::ConfigDefinition def{true}; - init_config(def, *params); - generate_common_config_comments(def); + // logging + def.add_section_comments( + "logging", + { + "logging settings", + }); - // oxend - def.add_section_comments( - "lokid", - { - "Settings for communicating with oxend", - }); + // api + def.add_section_comments( + "api", + { + "JSON API settings", + }); + + // dns + def.add_section_comments( + "dns", + { + "DNS configuration", + }); + + // bootstrap + def.add_section_comments( + "bootstrap", + { + "Configure nodes that will bootstrap us onto the network", + }); + + // network + def.add_section_comments( + "network", + { + "Network settings", + }); + } + + std::string Config::generate_client_config_base() + { + auto params = make_gen_params(); + params->is_relay = false; + params->default_data_dir = data_dir; + + llarp::ConfigDefinition def{false}; + init_config(def, *params); + generate_common_config_comments(def); + def.add_section_comments( + "paths", + { + "path selection algorithm options", + }); - return def.generate_ini_config(true); - } - - std::shared_ptr - Config::make_embedded_config() - { - auto config = std::make_shared(); - config->load(); - config->logging.level = log::Level::off; - config->api.enable_rpc_server = false; - config->network.endpoint_type = "null"; - config->network.save_profiles = false; - config->bootstrap.files.clear(); - return config; - } + def.add_section_comments( + "network", + { + "Snapp settings", + }); + + return def.generate_ini_config(true); + } + + std::string Config::generate_router_config_base() + { + auto params = make_gen_params(); + params->is_relay = true; + params->default_data_dir = data_dir; + + llarp::ConfigDefinition def{true}; + init_config(def, *params); + generate_common_config_comments(def); + + // oxend + def.add_section_comments( + "lokid", + { + "Settings for communicating with oxend", + }); + + return def.generate_ini_config(true); + } + + std::shared_ptr Config::make_embedded_config() + { + auto config = std::make_shared(); + config->load(); + config->logging.level = log::Level::off; + config->api.enable_rpc_server = false; + config->network.endpoint_type = "null"; + config->network.save_profiles = false; + config->bootstrap.files.clear(); + return config; + } } // namespace llarp diff --git a/llarp/config/config.hpp b/llarp/config/config.hpp index 152da9345..99182de59 100644 --- a/llarp/config/config.hpp +++ b/llarp/config/config.hpp @@ -31,284 +31,261 @@ namespace llarp { - using SectionValues = llarp::ConfigParser::SectionValues; - using ConfigMap = llarp::ConfigParser::ConfigMap; - - inline const std::string QUAD_ZERO{"0.0.0.0"}; - inline constexpr uint16_t DEFAULT_LISTEN_PORT{1090}; - inline constexpr int CLIENT_ROUTER_CONNECTIONS = 4; + using SectionValues = llarp::ConfigParser::SectionValues; + using ConfigMap = llarp::ConfigParser::ConfigMap; - // TODO: don't use these maps. they're sloppy and difficult to follow - /// Small struct to gather all parameters needed for config generation to reduce the number of - /// parameters that need to be passed around. - struct ConfigGenParameters - { - ConfigGenParameters() = default; - virtual ~ConfigGenParameters() = default; + inline const std::string QUAD_ZERO{"0.0.0.0"}; + inline constexpr uint16_t DEFAULT_LISTEN_PORT{1090}; + inline constexpr int CLIENT_ROUTER_CONNECTIONS = 4; - ConfigGenParameters(const ConfigGenParameters&) = delete; - ConfigGenParameters(ConfigGenParameters&&) = delete; + // TODO: don't use these maps. they're sloppy and difficult to follow + /// Small struct to gather all parameters needed for config generation to reduce the number of + /// parameters that need to be passed around. + struct ConfigGenParameters + { + ConfigGenParameters() = default; + virtual ~ConfigGenParameters() = default; - bool is_relay = false; - fs::path default_data_dir; + ConfigGenParameters(const ConfigGenParameters&) = delete; + ConfigGenParameters(ConfigGenParameters&&) = delete; - /// get network platform (virtual for unit test mocks) - virtual const llarp::net::Platform* - Net_ptr() const = 0; - }; + bool is_relay = false; + fs::path default_data_dir; - struct RouterConfig - { - int client_router_connections{CLIENT_ROUTER_CONNECTIONS}; + /// get network platform (virtual for unit test mocks) + virtual const llarp::net::Platform* Net_ptr() const = 0; + }; - std::string net_id; + struct RouterConfig + { + int client_router_connections{CLIENT_ROUTER_CONNECTIONS}; - fs::path data_dir; + std::string net_id; - bool block_bogons = false; + fs::path data_dir; - int worker_threads = -1; - int net_threads = -1; + bool block_bogons = false; - size_t job_que_size = 0; + int worker_threads = -1; + int net_threads = -1; - std::string rc_file; - std::string enckey_file; - std::string idkey_file; - std::string transkey_file; + size_t job_que_size = 0; - bool is_relay = false; + std::string rc_file; + std::string enckey_file; + std::string idkey_file; + std::string transkey_file; - std::optional public_ip; - std::optional public_port; + bool is_relay = false; - void - define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params); - }; + std::optional public_ip; + std::optional public_port; - /// config for path hop selection - struct PeerSelectionConfig - { - /// in our hops what netmask will we use for unique ips for hops - /// i.e. 32 for every hop unique ip, 24 unique /24 per hop, etc - /// - int unique_hop_netmask; + void define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params); + }; - /// set of countrys to exclude from path building (2 char country code) - std::unordered_set exclude_countries; + /// config for path hop selection + struct PeerSelectionConfig + { + /// in our hops what netmask will we use for unique ips for hops + /// i.e. 32 for every hop unique ip, 24 unique /24 per hop, etc + /// + int unique_hop_netmask; - void - define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params); + /// set of countrys to exclude from path building (2 char country code) + std::unordered_set exclude_countries; - /// return true if this set of router contacts is acceptable against this config - bool - check_rcs(const std::set& hops) const; - }; + void define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params); - struct NetworkConfig - { - std::optional enable_profiling; - bool save_profiles; - std::set strict_connect; - std::string if_name; - IPRange if_addr; + /// return true if this set of router contacts is acceptable against this config + bool check_rcs(const std::set& hops) const; + }; - std::optional keyfile; - std::string endpoint_type; - bool is_reachable = false; - std::optional hops; - std::optional paths; - bool allow_exit = false; - std::set snode_blacklist; - net::IPRangeMap exit_map; - net::IPRangeMap ons_exit_map; - - std::unordered_map exit_auths; - std::unordered_map ons_exit_auths; - - std::unordered_map map_addrs; - - service::AuthType auth_type = service::AuthType::NONE; - service::AuthFileType auth_file_type = service::AuthFileType::HASHES; - std::optional auth_url; - std::optional auth_method; - std::unordered_set auth_whitelist; - std::unordered_set auth_static_tokens; - std::set auth_files; - - std::vector srv_records; - - std::optional base_ipv6_addr; - - std::set owned_ranges; - std::optional traffic_policy; - - std::optional path_alignment_timeout; - - std::optional addr_map_persist_file; - - bool enable_route_poker; - bool blackhole_routes; - - void - define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params); - }; - - struct DnsConfig - { - bool raw; - std::vector bind_addr; - std::vector upstream_dns; - std::vector hostfiles; - std::optional query_bind; - - std::unordered_multimap extra_opts; - - void - define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params); - }; - - struct LinksConfig - { - // DEPRECATED -- use [Router]:public_addr - std::optional public_addr; - // DEPRECATED -- use [Router]:public_port - std::optional public_port; - - std::optional listen_addr; - - bool using_user_value = false; - bool using_new_api = false; - - void - define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params); - }; - - // TODO: remove oxenmq from this header - struct ApiConfig - { - bool enable_rpc_server = false; - std::vector rpc_bind_addrs; - - void - define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params); - }; - - struct LokidConfig - { - fs::path id_keyfile; - oxenmq::address rpc_addr; - bool disable_testing = true; - - void - define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params); - }; - - struct BootstrapConfig - { - std::vector files; - bool seednode; - - void - define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params); - }; - - struct LoggingConfig - { - log::Type type = log::Type::Print; - log::Level level = log::Level::off; - std::string file; - - void - define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params); - }; - - struct Config - { - explicit Config(std::optional datadir = std::nullopt); - - virtual ~Config() = default; - - /// create generation params (virtual for unit test mock) - virtual std::unique_ptr - make_gen_params() const; - - RouterConfig router; - NetworkConfig network; - PeerSelectionConfig paths; - DnsConfig dns; - LinksConfig links; - ApiConfig api; - LokidConfig lokid; - BootstrapConfig bootstrap; - LoggingConfig logging; - - // Initialize config definition - void - init_config(ConfigDefinition& conf, const ConfigGenParameters& params); - - /// Insert config entries for backwards-compatibility (e.g. so that the config system will - /// tolerate old values that are no longer accepted) - /// - /// @param conf is the config to modify - void - add_backcompat_opts(ConfigDefinition& conf); - - // Load a config from the given file if the config file is not provided LoadDefault is called - bool - load(std::optional fname = std::nullopt, bool isRelay = false); - - // Load a config from a string of ini, same effects as Config::Load - bool - load_string(std::string_view ini, bool isRelay = false); - - std::string - generate_client_config_base(); - - std::string - generate_router_config_base(); - - void - save(); - - void - override(std::string section, std::string key, std::string value); - - void - add_default(std::string section, std::string key, std::string value); - - /// create a config with the default parameters for an embedded lokinet - static std::shared_ptr - make_embedded_config(); - - private: - /// Load (initialize) a default config. - /// - /// This delegates to the ConfigDefinition to generate a default config, - /// as though an empty config were specified. - /// - /// If using Config without the intention of loading from file (or string), this is necessary - /// in order to obtain sane defaults. - /// - /// @param isRelay determines whether the config will reflect that of a relay or client - /// @param dataDir is a path representing a directory to be used as the data dir - /// @return true on success, false otherwise - bool - load_default_config(bool isRelay); - - bool - load_config_data( - std::string_view ini, std::optional fname = std::nullopt, bool isRelay = false); - - void - load_overrides(ConfigDefinition& conf) const; - - std::vector> additional; - ConfigParser parser; - const fs::path data_dir; - }; - - void - ensure_config(fs::path dataDir, fs::path confFile, bool overwrite, bool asRouter); + struct NetworkConfig + { + std::optional enable_profiling; + bool save_profiles; + std::set strict_connect; + std::string if_name; + IPRange if_addr; + + std::optional keyfile; + std::string endpoint_type; + bool is_reachable = false; + std::optional hops; + std::optional paths; + bool allow_exit = false; + std::set snode_blacklist; + net::IPRangeMap exit_map; + net::IPRangeMap ons_exit_map; + + std::unordered_map exit_auths; + std::unordered_map ons_exit_auths; + + std::unordered_map map_addrs; + + service::AuthType auth_type = service::AuthType::NONE; + service::AuthFileType auth_file_type = service::AuthFileType::HASHES; + std::optional auth_url; + std::optional auth_method; + std::unordered_set auth_whitelist; + std::unordered_set auth_static_tokens; + std::set auth_files; + + std::vector srv_records; + + std::optional base_ipv6_addr; + + std::set owned_ranges; + std::optional traffic_policy; + + std::optional path_alignment_timeout; + + std::optional addr_map_persist_file; + + bool enable_route_poker; + bool blackhole_routes; + + void define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params); + }; + + struct DnsConfig + { + bool raw; + std::vector bind_addr; + std::vector upstream_dns; + std::vector hostfiles; + std::optional query_bind; + + std::unordered_multimap extra_opts; + + void define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params); + }; + + struct LinksConfig + { + // DEPRECATED -- use [Router]:public_addr + std::optional public_addr; + // DEPRECATED -- use [Router]:public_port + std::optional public_port; + + std::optional listen_addr; + + bool using_user_value = false; + bool using_new_api = false; + + void define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params); + }; + + // TODO: remove oxenmq from this header + struct ApiConfig + { + bool enable_rpc_server = false; + std::vector rpc_bind_addrs; + + void define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params); + }; + + struct LokidConfig + { + fs::path id_keyfile; + oxenmq::address rpc_addr; + bool disable_testing = true; + + void define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params); + }; + + struct BootstrapConfig + { + std::vector files; + bool seednode; + + void define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params); + }; + + struct LoggingConfig + { + log::Type type = log::Type::Print; + log::Level level = log::Level::off; + std::string file; + + void define_config_options(ConfigDefinition& conf, const ConfigGenParameters& params); + }; + + struct Config + { + explicit Config(std::optional datadir = std::nullopt); + + virtual ~Config() = default; + + /// create generation params (virtual for unit test mock) + virtual std::unique_ptr make_gen_params() const; + + RouterConfig router; + NetworkConfig network; + PeerSelectionConfig paths; + DnsConfig dns; + LinksConfig links; + ApiConfig api; + LokidConfig lokid; + BootstrapConfig bootstrap; + LoggingConfig logging; + + // Initialize config definition + void init_config(ConfigDefinition& conf, const ConfigGenParameters& params); + + /// Insert config entries for backwards-compatibility (e.g. so that the config system will + /// tolerate old values that are no longer accepted) + /// + /// @param conf is the config to modify + void add_backcompat_opts(ConfigDefinition& conf); + + // Load a config from the given file if the config file is not provided LoadDefault is + // called + bool load(std::optional fname = std::nullopt, bool isRelay = false); + + // Load a config from a string of ini, same effects as Config::Load + bool load_string(std::string_view ini, bool isRelay = false); + + std::string generate_client_config_base(); + + std::string generate_router_config_base(); + + void save(); + + void override(std::string section, std::string key, std::string value); + + void add_default(std::string section, std::string key, std::string value); + + /// create a config with the default parameters for an embedded lokinet + static std::shared_ptr make_embedded_config(); + + private: + /// Load (initialize) a default config. + /// + /// This delegates to the ConfigDefinition to generate a default config, + /// as though an empty config were specified. + /// + /// If using Config without the intention of loading from file (or string), this is + /// necessary in order to obtain sane defaults. + /// + /// @param isRelay determines whether the config will reflect that of a relay or client + /// @param dataDir is a path representing a directory to be used as the data dir + /// @return true on success, false otherwise + bool load_default_config(bool isRelay); + + bool load_config_data( + std::string_view ini, + std::optional fname = std::nullopt, + bool isRelay = false); + + void load_overrides(ConfigDefinition& conf) const; + + std::vector> additional; + ConfigParser parser; + const fs::path data_dir; + }; + + void ensure_config(fs::path dataDir, fs::path confFile, bool overwrite, bool asRouter); } // namespace llarp diff --git a/llarp/config/definition.cpp b/llarp/config/definition.cpp index b4e1adac1..f38eecda0 100644 --- a/llarp/config/definition.cpp +++ b/llarp/config/definition.cpp @@ -8,285 +8,276 @@ namespace llarp { - template <> - bool - OptionDefinition::from_string(const std::string& input) - { - if (input == "false" || input == "off" || input == "0" || input == "no") - return false; - if (input == "true" || input == "on" || input == "1" || input == "yes") - return true; - throw std::invalid_argument{fmt::format("{} is not a valid bool", input)}; - } - - ConfigDefinition& - ConfigDefinition::define_option(std::unique_ptr def) - { - using namespace config; - // If explicitly deprecated or is a {client,relay} option in a {relay,client} config then add a - // dummy, warning option instead of this one. - if (def->deprecated || (relay ? def->clientOnly : def->relay_only)) + template <> + bool OptionDefinition::from_string(const std::string& input) { - return define_option( - def->section, - def->name, - MultiValue, - Hidden, - [deprecated = def->deprecated, - relay = relay, - opt = "[" + def->section + "]:" + def->name](std::string_view) { - LogWarn( - "*** WARNING: The config option ", - opt, - (deprecated ? " is deprecated" - : relay ? " is not valid in service node configuration files" - : " is not valid in client configuration files"), - " and has been ignored."); - }); + if (input == "false" || input == "off" || input == "0" || input == "no") + return false; + if (input == "true" || input == "on" || input == "1" || input == "yes") + return true; + throw std::invalid_argument{fmt::format("{} is not a valid bool", input)}; } - auto [sectionItr, newSect] = definitions.try_emplace(def->section); - if (newSect) - section_ordering.push_back(def->section); - auto& section = sectionItr->first; + ConfigDefinition& ConfigDefinition::define_option(std::unique_ptr def) + { + using namespace config; + // If explicitly deprecated or is a {client,relay} option in a {relay,client} config then + // add a dummy, warning option instead of this one. + if (def->deprecated || (relay ? def->clientOnly : def->relay_only)) + { + return define_option( + def->section, + def->name, + MultiValue, + Hidden, + [deprecated = def->deprecated, + relay = relay, + opt = "[" + def->section + "]:" + def->name](std::string_view) { + LogWarn( + "*** WARNING: The config option ", + opt, + (deprecated ? " is deprecated" + : relay ? " is not valid in service node configuration files" + : " is not valid in client configuration files"), + " and has been ignored."); + }); + } + + auto [sectionItr, newSect] = definitions.try_emplace(def->section); + if (newSect) + section_ordering.push_back(def->section); + auto& section = sectionItr->first; + + auto [it, added] = definitions[section].try_emplace(std::string{def->name}, std::move(def)); + if (!added) + throw std::invalid_argument{ + fmt::format("definition for [{}]:{} already exists", def->section, def->name)}; + + definition_ordering[section].push_back(it->first); + + if (!it->second->comments.empty()) + add_option_comments(section, it->first, std::move(it->second->comments)); + + return *this; + } - auto [it, added] = definitions[section].try_emplace(std::string{def->name}, std::move(def)); - if (!added) - throw std::invalid_argument{ - fmt::format("definition for [{}]:{} already exists", def->section, def->name)}; + ConfigDefinition& ConfigDefinition::add_config_value( + std::string_view section, std::string_view name, std::string_view value) + { + // see if we have an undeclared handler to fall back to in case section or section:name is + // absent + auto undItr = undeclared_handlers.find(std::string(section)); + bool haveUndeclaredHandler = (undItr != undeclared_handlers.end()); + + // get section, falling back to undeclared handler if needed + auto secItr = definitions.find(std::string(section)); + if (secItr == definitions.end()) + { + // fallback to undeclared handler if available + if (not haveUndeclaredHandler) + throw std::invalid_argument{fmt::format("unrecognized section [{}]", section)}; + auto& handler = undItr->second; + handler(section, name, value); + return *this; + } + + // section was valid, get definition by name + // fall back to undeclared handler if needed + auto& sectionDefinitions = secItr->second; + auto defItr = sectionDefinitions.find(std::string(name)); + if (defItr != sectionDefinitions.end()) + { + std::unique_ptr& definition = defItr->second; + definition->parse_value(std::string(value)); + return *this; + } + + if (not haveUndeclaredHandler) + throw std::invalid_argument{fmt::format("unrecognized option [{}]: {}", section, name)}; + + auto& handler = undItr->second; + handler(section, name, value); + return *this; + } - definition_ordering[section].push_back(it->first); + void ConfigDefinition::add_undeclared_handler( + const std::string& section, UndeclaredValueHandler handler) + { + auto itr = undeclared_handlers.find(section); + if (itr != undeclared_handlers.end()) + throw std::logic_error{fmt::format("section {} already has a handler", section)}; - if (!it->second->comments.empty()) - add_option_comments(section, it->first, std::move(it->second->comments)); + undeclared_handlers[section] = std::move(handler); + } - return *this; - } + void ConfigDefinition::remove_undeclared_handler(const std::string& section) + { + auto itr = undeclared_handlers.find(section); + if (itr != undeclared_handlers.end()) + undeclared_handlers.erase(itr); + } - ConfigDefinition& - ConfigDefinition::add_config_value( - std::string_view section, std::string_view name, std::string_view value) - { - // see if we have an undeclared handler to fall back to in case section or section:name is - // absent - auto undItr = undeclared_handlers.find(std::string(section)); - bool haveUndeclaredHandler = (undItr != undeclared_handlers.end()); + void ConfigDefinition::validate_required_fields() + { + visit_sections([&](const std::string& section, const DefinitionMap&) { + visit_definitions( + section, [&](const std::string&, const std::unique_ptr& def) { + if (def->required and def->get_number_found() < 1) + { + throw std::invalid_argument{ + fmt::format("[{}]:{} is required but missing", section, def->name)}; + } + + // should be handled earlier in OptionDefinition::parse_value() + assert(def->get_number_found() <= 1 or def->multi_valued); + }); + }); + } - // get section, falling back to undeclared handler if needed - auto secItr = definitions.find(std::string(section)); - if (secItr == definitions.end()) + void ConfigDefinition::accept_all_options() { - // fallback to undeclared handler if available - if (not haveUndeclaredHandler) - throw std::invalid_argument{fmt::format("unrecognized section [{}]", section)}; - auto& handler = undItr->second; - handler(section, name, value); - return *this; + visit_sections([this](const std::string& section, const DefinitionMap&) { + visit_definitions( + section, [](const std::string&, const std::unique_ptr& def) { + def->try_accept(); + }); + }); } - // section was valid, get definition by name - // fall back to undeclared handler if needed - auto& sectionDefinitions = secItr->second; - auto defItr = sectionDefinitions.find(std::string(name)); - if (defItr != sectionDefinitions.end()) + void ConfigDefinition::add_section_comments( + const std::string& section, std::vector comments) { - std::unique_ptr& definition = defItr->second; - definition->parse_value(std::string(value)); - return *this; + auto& sectionComments = section_comments[section]; + for (auto& c : comments) + sectionComments.emplace_back(std::move(c)); } - if (not haveUndeclaredHandler) - throw std::invalid_argument{fmt::format("unrecognized option [{}]: {}", section, name)}; - - auto& handler = undItr->second; - handler(section, name, value); - return *this; - } - - void - ConfigDefinition::add_undeclared_handler( - const std::string& section, UndeclaredValueHandler handler) - { - auto itr = undeclared_handlers.find(section); - if (itr != undeclared_handlers.end()) - throw std::logic_error{fmt::format("section {} already has a handler", section)}; - - undeclared_handlers[section] = std::move(handler); - } - - void - ConfigDefinition::remove_undeclared_handler(const std::string& section) - { - auto itr = undeclared_handlers.find(section); - if (itr != undeclared_handlers.end()) - undeclared_handlers.erase(itr); - } - - void - ConfigDefinition::validate_required_fields() - { - visit_sections([&](const std::string& section, const DefinitionMap&) { - visit_definitions( - section, [&](const std::string&, const std::unique_ptr& def) { - if (def->required and def->get_number_found() < 1) - { - throw std::invalid_argument{ - fmt::format("[{}]:{} is required but missing", section, def->name)}; - } + void ConfigDefinition::add_option_comments( + const std::string& section, const std::string& name, std::vector comments) + { + auto& defComments = definition_comments[section][name]; + if (defComments.empty()) + defComments = std::move(comments); + else + defComments.insert( + defComments.end(), + std::make_move_iterator(comments.begin()), + std::make_move_iterator(comments.end())); + } - // should be handled earlier in OptionDefinition::parse_value() - assert(def->get_number_found() <= 1 or def->multi_valued); - }); - }); - } - - void - ConfigDefinition::accept_all_options() - { - visit_sections([this](const std::string& section, const DefinitionMap&) { - visit_definitions( - section, [](const std::string&, const std::unique_ptr& def) { - def->try_accept(); - }); - }); - } - - void - ConfigDefinition::add_section_comments( - const std::string& section, std::vector comments) - { - auto& sectionComments = section_comments[section]; - for (auto& c : comments) - sectionComments.emplace_back(std::move(c)); - } - - void - ConfigDefinition::add_option_comments( - const std::string& section, const std::string& name, std::vector comments) - { - auto& defComments = definition_comments[section][name]; - if (defComments.empty()) - defComments = std::move(comments); - else - defComments.insert( - defComments.end(), - std::make_move_iterator(comments.begin()), - std::make_move_iterator(comments.end())); - } - - std::string - ConfigDefinition::generate_ini_config(bool useValues) - { - std::string ini; - auto ini_append = std::back_inserter(ini); - - int sectionsVisited = 0; - - visit_sections([&](const std::string& section, const DefinitionMap&) { - std::string sect_str; - auto sect_append = std::back_inserter(sect_str); - - visit_definitions( - section, [&](const std::string& name, const std::unique_ptr& def) { - bool has_comment = false; - // TODO: as above, this will create empty objects - // TODO: as above (but more important): this won't handle definitions with no entries - // (i.e. those handled by UndeclaredValueHandler's) - for (const std::string& comment : definition_comments[section][name]) + std::string ConfigDefinition::generate_ini_config(bool useValues) + { + std::string ini; + auto ini_append = std::back_inserter(ini); + + int sectionsVisited = 0; + + visit_sections([&](const std::string& section, const DefinitionMap&) { + std::string sect_str; + auto sect_append = std::back_inserter(sect_str); + + visit_definitions( + section, + [&](const std::string& name, const std::unique_ptr& def) { + bool has_comment = false; + // TODO: as above, this will create empty objects + // TODO: as above (but more important): this won't handle definitions with no + // entries + // (i.e. those handled by UndeclaredValueHandler's) + for (const std::string& comment : definition_comments[section][name]) + { + fmt::format_to(sect_append, "\n# {}", comment); + has_comment = true; + } + + if (useValues and def->get_number_found() > 0) + { + for (const auto& val : def->values_as_string()) + fmt::format_to(sect_append, "\n{}={}", name, val); + *sect_append = '\n'; + } + else if (not def->hidden) + { + if (auto defaults = def->default_values_as_string(); not defaults.empty()) + for (const auto& val : defaults) + fmt::format_to( + sect_append, "\n{}{}={}", def->required ? "" : "#", name, val); + else + // We have no defaults so we append it as "#opt-name=" so that we show + // the option name, and make it simple to uncomment and edit to the + // desired value. + fmt::format_to(sect_append, "\n#{}=", name); + *sect_append = '\n'; + } + else if (has_comment) + *sect_append = '\n'; + }); + + if (sect_str.empty()) + return; // Skip sections with no options + + if (sectionsVisited > 0) + ini += "\n\n"; + + fmt::format_to(ini_append, "[{}]\n", section); + + // TODO: this will create empty objects as a side effect of map's operator[] + // TODO: this also won't handle sections which have no definition + for (const std::string& comment : section_comments[section]) { - fmt::format_to(sect_append, "\n# {}", comment); - has_comment = true; + fmt::format_to(ini_append, "# {}\n", comment); } + ini += "\n"; + ini += sect_str; - if (useValues and def->get_number_found() > 0) - { - for (const auto& val : def->values_as_string()) - fmt::format_to(sect_append, "\n{}={}", name, val); - *sect_append = '\n'; - } - else if (not def->hidden) - { - if (auto defaults = def->default_values_as_string(); not defaults.empty()) - for (const auto& val : defaults) - fmt::format_to(sect_append, "\n{}{}={}", def->required ? "" : "#", name, val); - else - // We have no defaults so we append it as "#opt-name=" so that we show the option - // name, and make it simple to uncomment and edit to the desired value. - fmt::format_to(sect_append, "\n#{}=", name); - *sect_append = '\n'; - } - else if (has_comment) - *sect_append = '\n'; - }); - - if (sect_str.empty()) - return; // Skip sections with no options - - if (sectionsVisited > 0) - ini += "\n\n"; - - fmt::format_to(ini_append, "[{}]\n", section); - - // TODO: this will create empty objects as a side effect of map's operator[] - // TODO: this also won't handle sections which have no definition - for (const std::string& comment : section_comments[section]) - { - fmt::format_to(ini_append, "# {}\n", comment); - } - ini += "\n"; - ini += sect_str; - - sectionsVisited++; - }); - - return ini; - } - - const std::unique_ptr& - ConfigDefinition::lookup_definition_or_throw( - std::string_view section, std::string_view name) const - { - const auto sectionItr = definitions.find(std::string(section)); - if (sectionItr == definitions.end()) - throw std::invalid_argument{fmt::format("No config section [{}]", section)}; - - auto& sectionDefinitions = sectionItr->second; - const auto definitionItr = sectionDefinitions.find(std::string(name)); - if (definitionItr == sectionDefinitions.end()) - throw std::invalid_argument{ - fmt::format("No config item {} within section {}", name, section)}; - - return definitionItr->second; - } - - std::unique_ptr& - ConfigDefinition::lookup_definition_or_throw(std::string_view section, std::string_view name) - { - return const_cast&>( - const_cast(this)->lookup_definition_or_throw(section, name)); - } - - void - ConfigDefinition::visit_sections(SectionVisitor visitor) const - { - for (const std::string& section : section_ordering) + sectionsVisited++; + }); + + return ini; + } + + const std::unique_ptr& ConfigDefinition::lookup_definition_or_throw( + std::string_view section, std::string_view name) const { - const auto itr = definitions.find(section); - assert(itr != definitions.end()); - visitor(section, itr->second); + const auto sectionItr = definitions.find(std::string(section)); + if (sectionItr == definitions.end()) + throw std::invalid_argument{fmt::format("No config section [{}]", section)}; + + auto& sectionDefinitions = sectionItr->second; + const auto definitionItr = sectionDefinitions.find(std::string(name)); + if (definitionItr == sectionDefinitions.end()) + throw std::invalid_argument{ + fmt::format("No config item {} within section {}", name, section)}; + + return definitionItr->second; } - }; - void - ConfigDefinition::visit_definitions(const std::string& section, DefVisitor visitor) const - { - const auto& defs = definitions.at(section); - const auto& defOrdering = definition_ordering.at(section); - for (const std::string& name : defOrdering) + + std::unique_ptr& ConfigDefinition::lookup_definition_or_throw( + std::string_view section, std::string_view name) { - const auto itr = defs.find(name); - assert(itr != defs.end()); - visitor(name, itr->second); + return const_cast&>( + const_cast(this)->lookup_definition_or_throw(section, name)); } - }; + + void ConfigDefinition::visit_sections(SectionVisitor visitor) const + { + for (const std::string& section : section_ordering) + { + const auto itr = definitions.find(section); + assert(itr != definitions.end()); + visitor(section, itr->second); + } + }; + void ConfigDefinition::visit_definitions(const std::string& section, DefVisitor visitor) const + { + const auto& defs = definitions.at(section); + const auto& defOrdering = definition_ordering.at(section); + for (const std::string& name : defOrdering) + { + const auto itr = defs.find(name); + assert(itr != defs.end()); + visitor(name, itr->second); + } + }; } // namespace llarp diff --git a/llarp/config/definition.hpp b/llarp/config/definition.hpp index 7b777f056..19e177f63 100644 --- a/llarp/config/definition.hpp +++ b/llarp/config/definition.hpp @@ -20,586 +20,565 @@ namespace llarp { - namespace config - { - namespace flag + namespace config { - // Base class for the following option flag types - struct opt - {}; - - struct REQUIRED : opt - {}; - struct HIDDEN : opt - {}; - struct MULTIVALUE : opt - {}; - struct RELAYONLY : opt - {}; - struct CLIENTONLY : opt - {}; - struct DEPRECATED : opt - {}; - } // namespace flag - - /// Value to pass for an OptionDefinition to indicate that the option is required - inline constexpr flag::REQUIRED Required{}; - /// Value to pass for an OptionDefinition to indicate that the option should be hidden from the - /// generate config file if it is unset (and has no comment). Typically for deprecated, renamed - /// options that still do something, and for internal dev options that aren't usefully exposed. - /// (For do-nothing deprecated options use Deprecated instead). - inline constexpr flag::HIDDEN Hidden{}; - /// Value to pass for an OptionDefinition to indicate that the option takes multiple values - inline constexpr flag::MULTIVALUE MultiValue{}; - /// Value to pass for an option that should only be set for relay configs. If found in a client - /// config it be ignored (but will produce a warning). - inline constexpr flag::RELAYONLY RelayOnly{}; - /// Value to pass for an option that should only be set for client configs. If found in a relay - /// config it will be ignored (but will produce a warning). - inline constexpr flag::CLIENTONLY ClientOnly{}; - /// Value to pass for an option that is deprecated and does nothing and should be ignored (with - /// a deprecation warning) if specified. Note that Deprecated implies Hidden, and that - /// {client,relay}-only options in a {relay,client} config are also considered Deprecated. - inline constexpr flag::DEPRECATED Deprecated{}; - - /// Wrapper to specify a default value to an OptionDefinition - template - struct Default - { - T val; - constexpr explicit Default(T val) : val{std::move(val)} - {} - }; + namespace flag + { + // Base class for the following option flag types + struct opt + {}; + + struct REQUIRED : opt + {}; + struct HIDDEN : opt + {}; + struct MULTIVALUE : opt + {}; + struct RELAYONLY : opt + {}; + struct CLIENTONLY : opt + {}; + struct DEPRECATED : opt + {}; + } // namespace flag + + /// Value to pass for an OptionDefinition to indicate that the option is required + inline constexpr flag::REQUIRED Required{}; + /// Value to pass for an OptionDefinition to indicate that the option should be hidden from + /// the generate config file if it is unset (and has no comment). Typically for deprecated, + /// renamed options that still do something, and for internal dev options that aren't + /// usefully exposed. (For do-nothing deprecated options use Deprecated instead). + inline constexpr flag::HIDDEN Hidden{}; + /// Value to pass for an OptionDefinition to indicate that the option takes multiple values + inline constexpr flag::MULTIVALUE MultiValue{}; + /// Value to pass for an option that should only be set for relay configs. If found in a + /// client config it be ignored (but will produce a warning). + inline constexpr flag::RELAYONLY RelayOnly{}; + /// Value to pass for an option that should only be set for client configs. If found in a + /// relay config it will be ignored (but will produce a warning). + inline constexpr flag::CLIENTONLY ClientOnly{}; + /// Value to pass for an option that is deprecated and does nothing and should be ignored + /// (with a deprecation warning) if specified. Note that Deprecated implies Hidden, and + /// that {client,relay}-only options in a {relay,client} config are also considered + /// Deprecated. + inline constexpr flag::DEPRECATED Deprecated{}; + + /// Wrapper to specify a default value to an OptionDefinition + template + struct Default + { + T val; + constexpr explicit Default(T val) : val{std::move(val)} + {} + }; - /// Adds one or more comment lines to the option definition. - struct Comment + /// Adds one or more comment lines to the option definition. + struct Comment + { + std::vector comments; + explicit Comment(std::initializer_list comments) + : comments{std::move(comments)} + {} + }; + + /// A convenience function that returns an acceptor which assigns to a reference. + /// + /// Note that this holds on to the reference; it must only be used when this is safe to do. + /// In particular, a reference to a local variable may be problematic. + template + auto assignment_acceptor(T& ref) + { + return [&ref](T arg) { ref = std::move(arg); }; + } + + // C++20 backport: + template + using remove_cvref_t = std::remove_cv_t>; + + template + constexpr bool is_default = false; + template + constexpr bool is_default> = true; + template + constexpr bool is_default = is_default>; + + template + constexpr bool is_default_array = false; + template + constexpr bool is_default_array, N>> = true; + template + constexpr bool is_default_array = is_default_array>; + + template + constexpr bool is_option = std::is_base_of_v> + or std::is_same_v or is_default