From 9e2489fdd70626651cef3dbdef0968347edf14b1 Mon Sep 17 00:00:00 2001 From: iceboy Date: Sun, 10 Nov 2024 14:17:17 -0800 Subject: [PATCH] net/proxy add config parser --- WORKSPACE | 2 +- net/proxy/route/BUILD | 2 +- net/proxy/route/config.cc | 85 +++++++++++++++++------------- net/proxy/shadowsocks/BUILD | 2 +- net/proxy/shadowsocks/config.cc | 89 +++++++++++++++++++++---------- net/proxy/socks/BUILD | 2 +- net/proxy/socks/config.cc | 25 ++++++--- net/proxy/system/BUILD | 2 +- net/proxy/system/config.cc | 57 ++++++++++++++------ net/proxy/util/BUILD | 11 ++++ net/proxy/util/config.cc | 63 ++++++++++++++++++++++ net/proxy/util/config.h | 93 +++++++++++++++++++++++++++++++++ 12 files changed, 344 insertions(+), 89 deletions(-) create mode 100644 net/proxy/util/config.cc create mode 100644 net/proxy/util/config.h diff --git a/WORKSPACE b/WORKSPACE index 93eede2..ce21f94 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -38,7 +38,7 @@ http_archive( git_repository( name = "org_boost_boost", - commit = "2a72734ebe29e1c2abcf5b84443e385b20071e8d", + commit = "b0d38289e8ed571ab078172194aad5980fff515e", remote = "https://github.com/iceboy233/boost.git", ) diff --git a/net/proxy/route/BUILD b/net/proxy/route/BUILD index 444584f..41921db 100644 --- a/net/proxy/route/BUILD +++ b/net/proxy/route/BUILD @@ -6,7 +6,7 @@ cc_library( deps = [ ":connector", "//net/proxy", - "@org_boost_boost//:property_tree", + "//net/proxy/util:config", "@org_iceboy_trunk//base:logging", ], alwayslink = 1, diff --git a/net/proxy/route/config.cc b/net/proxy/route/config.cc index e8e95b0..82e51e1 100644 --- a/net/proxy/route/config.cc +++ b/net/proxy/route/config.cc @@ -1,54 +1,67 @@ #include +#include #include -#include +#include #include "base/logging.h" #include "net/proxy/proxy.h" #include "net/proxy/registry.h" #include "net/proxy/route/connector.h" +#include "net/proxy/util/config.h" namespace net { namespace proxy { -namespace route { -namespace { -Connector::Rule parse_rule( - Proxy &proxy, - const boost::property_tree::ptree &rule_config) { - Connector::Rule rule; - for (auto iters = rule_config.equal_range("host"); - iters.first != iters.second; - ++iters.first) { - std::string host = iters.first->second.get_value(); - rule.hosts.push_back(std::move(host)); - } - for (auto iters = rule_config.equal_range("host-suffix"); - iters.first != iters.second; - ++iters.first) { - std::string host_suffix = iters.first->second.get_value(); - rule.host_suffixes.push_back(std::move(host_suffix)); - } - if (rule_config.get("default", false)) { - rule.is_default = true; +struct RouteConnectorRuleConfig { + std::vector host; + std::vector host_suffix; + bool default_ = false; + bool drop = false; + std::string connector; +}; + +template <> +struct ConfigVisitor { + template + void operator()(V &&v, RouteConnectorRuleConfig &c) const { + v("host", c.host); + v("host-suffix", c.host_suffix); + v("default", c.default_); + v("drop", c.drop); + v("connector", c.connector); } - if (!rule_config.get("drop", false)) { - std::string connector = rule_config.get("connector", ""); - rule.connector = proxy.get_connector(connector); - if (!rule.connector) { - LOG(error) << "invalid connector: " << connector; - } +}; + +struct RouteConnectorConfig { + std::vector rule; +}; + +template <> +struct ConfigVisitor { + template + void operator()(V &&v, RouteConnectorConfig &c) const { + v("rule", c.rule); } - return rule; -} +}; + +namespace route { +namespace { -REGISTER_CONNECTOR(route, []( - Proxy &proxy, - const boost::property_tree::ptree &config) -> std::unique_ptr { +REGISTER_CONNECTOR(route, [](Proxy &proxy, const auto &ptree) { + auto config = parse_connector_config(ptree); std::vector rules; - for (auto iters = config.equal_range("rule"); - iters.first != iters.second; - ++iters.first) { - rules.push_back(parse_rule(proxy, iters.first->second)); + for (const RouteConnectorRuleConfig &rule_config : config.rule) { + Connector::Rule rule; + rule.hosts = rule_config.host; + rule.host_suffixes = rule_config.host_suffix; + rule.is_default = rule_config.default_; + if (!rule_config.drop) { + rule.connector = proxy.get_connector(rule_config.connector); + if (!rule.connector) { + LOG(error) << "invalid connector: " << rule_config.connector; + } + } + rules.push_back(std::move(rule)); } return std::make_unique(rules); }); diff --git a/net/proxy/shadowsocks/BUILD b/net/proxy/shadowsocks/BUILD index 31c4d6e..f9c0ee9 100644 --- a/net/proxy/shadowsocks/BUILD +++ b/net/proxy/shadowsocks/BUILD @@ -7,7 +7,7 @@ cc_library( ":connector", ":handler", "//net/proxy", - "@org_boost_boost//:property_tree", + "//net/proxy/util:config", "@org_iceboy_trunk//base:logging", ], alwayslink = 1, diff --git a/net/proxy/shadowsocks/config.cc b/net/proxy/shadowsocks/config.cc index c04119b..3f2735a 100644 --- a/net/proxy/shadowsocks/config.cc +++ b/net/proxy/shadowsocks/config.cc @@ -1,32 +1,72 @@ +#include #include -#include +#include +#include #include "base/logging.h" #include "net/proxy/proxy.h" #include "net/proxy/registry.h" #include "net/proxy/shadowsocks/connector.h" #include "net/proxy/shadowsocks/handler.h" +#include "net/proxy/util/config.h" namespace net { namespace proxy { + +struct ShadowsocksHandlerConfig { + std::string method; + std::string password; + std::string connector; +}; + +template <> +struct ConfigVisitor { + template + void operator()(V &&v, ShadowsocksHandlerConfig &c) const { + v("method", c.method); + v("password", c.password); + v("connector", c.connector); + } +}; + +struct ShadowsocksConnectorConfig { + std::vector server; + std::string method; + std::string password; + uint32_t min_padding_length = 1; + uint32_t max_padding_length = 900; + std::string connector; +}; + +template <> +struct ConfigVisitor { + template + void operator()(V &&v, ShadowsocksConnectorConfig &c) const { + v("server", c.server); + v("method", c.method); + v("password", c.password); + v("min-padding-length", c.min_padding_length); + v("max-padding-length", c.max_padding_length); + v("connector", c.connector); + } +}; + namespace shadowsocks { namespace { REGISTER_HANDLER(shadowsocks, []( - Proxy &proxy, - const boost::property_tree::ptree &config) -> std::unique_ptr { + Proxy &proxy, const auto &ptree) -> std::unique_ptr { + auto config = parse_handler_config(ptree); Handler::InitOptions options; - std::string method = config.get("method", ""); - options.method = Method::find(method); + options.method = Method::find(config.method); if (!options.method) { - LOG(error) << "invalid method: " << method; + LOG(error) << "invalid method: " << config.method; return nullptr; } - options.password = config.get("password", ""); - std::string connector_str = config.get("connector", ""); - proxy::Connector *connector = proxy.get_connector(connector_str); + options.password = config.password; + proxy::Connector *connector = proxy.get_connector(config.connector); if (!connector) { - LOG(error) << "invalid connector: " << connector_str; + LOG(error) << "invalid connector: " << config.connector; return nullptr; } auto handler = std::make_unique(*connector); @@ -38,33 +78,28 @@ REGISTER_HANDLER(shadowsocks, []( }); REGISTER_CONNECTOR(shadowsocks, []( - Proxy &proxy, - const boost::property_tree::ptree &config) -> std::unique_ptr { + Proxy &proxy, const auto &ptree) -> std::unique_ptr { + auto config = parse_connector_config(ptree); Connector::InitOptions options; - for (auto iters = config.equal_range("server"); - iters.first != iters.second; - ++iters.first) { - std::string server_str = iters.first->second.get_value(); - auto server_endpoint = Endpoint::from_string(server_str); + for (const std::string &server : config.server) { + auto server_endpoint = Endpoint::from_string(server); if (!server_endpoint) { - LOG(error) << "invalid server endpoint: " << server_str; + LOG(error) << "invalid server: " << server; continue; } options.endpoints.push_back(*server_endpoint); } - std::string method = config.get("method", ""); - options.method = Method::find(method); + options.method = Method::find(config.method); if (!options.method) { - LOG(error) << "invalid method: " << method; + LOG(error) << "invalid method: " << config.method; return nullptr; } - options.password = config.get("password", ""); - options.min_padding_length = config.get("min-padding-length", 1); - options.max_padding_length = config.get("max-padding-length", 900); - std::string connector_str = config.get("connector", ""); - proxy::Connector *base_connector = proxy.get_connector(connector_str); + options.password = config.password; + options.min_padding_length = config.min_padding_length; + options.max_padding_length = config.max_padding_length; + proxy::Connector *base_connector = proxy.get_connector(config.connector); if (!base_connector) { - LOG(error) << "invalid connector: " << connector_str; + LOG(error) << "invalid connector: " << config.connector; return nullptr; } auto connector = std::make_unique(*base_connector); diff --git a/net/proxy/socks/BUILD b/net/proxy/socks/BUILD index 277729d..32a473f 100644 --- a/net/proxy/socks/BUILD +++ b/net/proxy/socks/BUILD @@ -6,8 +6,8 @@ cc_library( deps = [ ":handler", "//net/proxy", + "//net/proxy/util:config", "@org_iceboy_trunk//base:logging", - "@org_boost_boost//:property_tree", ], alwayslink = 1, ) diff --git a/net/proxy/socks/config.cc b/net/proxy/socks/config.cc index 82f114b..78ddf7e 100644 --- a/net/proxy/socks/config.cc +++ b/net/proxy/socks/config.cc @@ -1,23 +1,36 @@ #include -#include +#include #include "base/logging.h" #include "net/proxy/proxy.h" #include "net/proxy/registry.h" #include "net/proxy/socks/handler.h" +#include "net/proxy/util/config.h" namespace net { namespace proxy { + +struct SocksHandlerConfig { + std::string connector; +}; + +template <> +struct ConfigVisitor { + template + void operator()(V &&v, SocksHandlerConfig &c) const { + v("connector", c.connector); + } +}; + namespace socks { namespace { REGISTER_HANDLER(socks, []( - Proxy &proxy, - const boost::property_tree::ptree &config) -> std::unique_ptr { - std::string connector_str = config.get("connector", ""); - Connector *connector = proxy.get_connector(connector_str); + Proxy &proxy, const auto &ptree) -> std::unique_ptr { + auto config = parse_handler_config(ptree); + Connector *connector = proxy.get_connector(config.connector); if (!connector) { - LOG(error) << "invalid connector: " << connector_str; + LOG(error) << "invalid connector: " << config.connector; return nullptr; } return std::make_unique(proxy.executor(), *connector); diff --git a/net/proxy/system/BUILD b/net/proxy/system/BUILD index 98571d8..b8562f7 100644 --- a/net/proxy/system/BUILD +++ b/net/proxy/system/BUILD @@ -6,7 +6,7 @@ cc_library( deps = [ ":connector", "//net/proxy", - "@org_boost_boost//:property_tree", + "//net/proxy/util:config", "@org_iceboy_trunk//base:logging", ], alwayslink = 1, diff --git a/net/proxy/system/config.cc b/net/proxy/system/config.cc index c65c35c..ca1ff09 100644 --- a/net/proxy/system/config.cc +++ b/net/proxy/system/config.cc @@ -1,39 +1,66 @@ #include #include -#include +#include +#include #include "base/logging.h" #include "net/proxy/ares/resolver.h" #include "net/proxy/proxy.h" #include "net/proxy/registry.h" #include "net/proxy/system/connector.h" +#include "net/proxy/util/config.h" namespace net { namespace proxy { + +struct ResolverConfig { + std::vector server; + std::string address_family = "prefer-v4"; +}; + +template <> +struct ConfigVisitor { + template + void operator()(V &&v, ResolverConfig &c) const { + v("server", c.server); + v("address-family", c.address_family); + } +}; + +struct SystemConnectorConfig { + double timeout = 300; + bool tcp_no_delay = true; + ResolverConfig resolver; +}; + +template <> +struct ConfigVisitor { + template + void operator()(V &&v, SystemConnectorConfig &c) const { + v("timeout", c.timeout); + v("tcp-no-delay", c.tcp_no_delay); + v("resolver", c.resolver); + } +}; + namespace system { namespace { -REGISTER_CONNECTOR(system, []( - Proxy &proxy, const boost::property_tree::ptree &config) { +REGISTER_CONNECTOR(system, [](Proxy &proxy, const auto &ptree) { + auto config = parse_connector_config(ptree); Connector::Options options; options.timeout = std::chrono::duration_cast( - std::chrono::duration(config.get("timeout", 300))); - options.tcp_no_delay = config.get("tcp_no_delay", true); - const boost::property_tree::ptree empty_ptree; - const auto &resolver_config = config.get_child("resolver", empty_ptree); - for (auto iters = resolver_config.equal_range("server"); - iters.first != iters.second; - ++iters.first) { - std::string server_str = iters.first->second.get_value(); - auto server_endpoint = Endpoint::from_string(server_str); + std::chrono::duration(config.timeout)); + options.tcp_no_delay = config.tcp_no_delay; + for (const std::string &server : config.resolver.server) { + auto server_endpoint = Endpoint::from_string(server); if (!server_endpoint) { - LOG(error) << "invalid server: " << server_str; + LOG(error) << "invalid server: " << server; continue; } options.resolver_options.servers.push_back(*server_endpoint); } - auto address_family = resolver_config.get( - "address-family", "prefer-v4"); + const std::string &address_family = config.resolver.address_family; if (address_family == "prefer-v4") { options.resolver_options.address_family = ares::Resolver::AddressFamily::prefer_v4; diff --git a/net/proxy/util/BUILD b/net/proxy/util/BUILD index 7261b81..e0b1027 100644 --- a/net/proxy/util/BUILD +++ b/net/proxy/util/BUILD @@ -1,5 +1,16 @@ package(default_visibility = ["//visibility:public"]) +cc_library( + name = "config", + srcs = ["config.cc"], + hdrs = ["config.h"], + deps = [ + "@com_google_absl//absl/container:flat_hash_set", + "@org_boost_boost//:property_tree", + "@org_iceboy_trunk//base:logging", + ], +) + cc_library( name = "copy", srcs = ["copy.cc"], diff --git a/net/proxy/util/config.cc b/net/proxy/util/config.cc new file mode 100644 index 0000000..8408555 --- /dev/null +++ b/net/proxy/util/config.cc @@ -0,0 +1,63 @@ +#include "net/proxy/util/config.h" + +#include "base/logging.h" + +namespace net { +namespace proxy { + +boost::property_tree::ptree PropertyTreeVisitor::empty_ptree_; + +PropertyTreeVisitor::PropertyTreeVisitor( + const boost::property_tree::ptree &ptree, + absl::flat_hash_set known_fields) + : ptree_(ptree), + known_fields_(std::move(known_fields)) {} + +void PropertyTreeVisitor::operator()(const char *name, bool &value) { + if (auto optional = ptree_.get_optional(name); optional) { + value = optional.value(); + } + known_fields_.insert(name); +} + +void PropertyTreeVisitor::operator()(const char *name, uint32_t &value) { + if (auto optional = ptree_.get_optional(name); optional) { + value = optional.value(); + } + known_fields_.insert(name); +} + +void PropertyTreeVisitor::operator()(const char *name, double &value) { + if (auto optional = ptree_.get_optional(name); optional) { + value = optional.value(); + } + known_fields_.insert(name); +} + +void PropertyTreeVisitor::operator()(const char *name, std::string &value) { + if (auto optional = ptree_.get_optional(name); optional) { + value = optional.value(); + } + known_fields_.insert(name); +} + +void PropertyTreeVisitor::operator()( + const char *name, std::vector &value) { + value.clear(); + for (auto iters = ptree_.equal_range(name); + iters.first != iters.second; ++iters.first) { + value.push_back(iters.first->second.get_value()); + } + known_fields_.insert(name); +} + +void PropertyTreeVisitor::log_unknown_fields() const { + for (const auto &pair : ptree_) { + if (!known_fields_.contains(pair.first)) { + LOG(warning) << "unknown field: " << pair.first; + } + } +} + +} // namespace proxy +} // namespace net diff --git a/net/proxy/util/config.h b/net/proxy/util/config.h new file mode 100644 index 0000000..ad8120b --- /dev/null +++ b/net/proxy/util/config.h @@ -0,0 +1,93 @@ +#ifndef _NET_PROXY_UTIL_CONFIG_H +#define _NET_PROXY_UTIL_CONFIG_H + +#include +#include +#include +#include +#include +#include +#include + +#include "absl/container/flat_hash_set.h" + +namespace net { +namespace proxy { + +template +struct ConfigVisitor {}; + +class PropertyTreeVisitor { +public: + PropertyTreeVisitor( + const boost::property_tree::ptree &ptree, + absl::flat_hash_set known_fields); + + void operator()(const char *name, bool &value); + void operator()(const char *name, uint32_t &value); + void operator()(const char *name, double &value); + void operator()(const char *name, std::string &value); + void operator()(const char *name, std::vector &value); + + template < + typename ConfigT, + typename = std::enable_if_t()( + std::declval(), + std::declval()))>>> + void operator()(const char *name, ConfigT &config) { + parse_config(ptree_.get_child(name, empty_ptree_), config); + known_fields_.insert(name); + } + + template < + typename ConfigT, + typename = std::enable_if_t()( + std::declval(), + std::declval()))>>> + void operator()(const char *name, std::vector &configs) { + configs.clear(); + for (auto iters = ptree_.equal_range(name); + iters.first != iters.second; ++iters.first) { + parse_config(iters.first->second, configs.emplace_back()); + } + known_fields_.insert(name); + } + + void log_unknown_fields() const; + +private: + static boost::property_tree::ptree empty_ptree_; + const boost::property_tree::ptree &ptree_; + absl::flat_hash_set known_fields_; +}; + +template +void parse_config( + const boost::property_tree::ptree &ptree, + ConfigT &config, + absl::flat_hash_set known_fields = {}) { + PropertyTreeVisitor ptree_visitor(ptree, std::move(known_fields)); + ConfigVisitor()(ptree_visitor, config); + ptree_visitor.log_unknown_fields(); +} + +template +ConfigT parse_connector_config(const boost::property_tree::ptree &ptree) { + ConfigT config; + parse_config(ptree, config, {"type"}); + return config; +} + +template +ConfigT parse_handler_config(const boost::property_tree::ptree &ptree) { + ConfigT config; + parse_config(ptree, config, {"listen", "type", "timeout", "tcp_no_delay"}); + return config; +} + +} // namespace proxy +} // namespace net + +#endif // _NET_PROXY_UTIL_CONFIG_H