You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@trafficserver.apache.org by am...@apache.org on 2022/12/04 04:24:35 UTC
[trafficserver] branch 10-Dev updated: libswoc: update IPAllow. (#9193)
This is an automated email from the ASF dual-hosted git repository.
amc pushed a commit to branch 10-Dev
in repository https://gitbox.apache.org/repos/asf/trafficserver.git
The following commit(s) were added to refs/heads/10-Dev by this push:
new 74dd418ea libswoc: update IPAllow. (#9193)
74dd418ea is described below
commit 74dd418ea35cdae723fe17f7a3d84db51418e668
Author: Alan M. Carroll <am...@apache.org>
AuthorDate: Sat Dec 3 22:24:28 2022 -0600
libswoc: update IPAllow. (#9193)
---
proxy/IPAllow.cc | 589 ++++++++++++++++++++-----------------------------------
proxy/IPAllow.h | 115 +++++++----
2 files changed, 289 insertions(+), 415 deletions(-)
diff --git a/proxy/IPAllow.cc b/proxy/IPAllow.cc
index 33917af14..f0d3bd7e4 100644
--- a/proxy/IPAllow.cc
+++ b/proxy/IPAllow.cc
@@ -25,83 +25,40 @@
*/
#include <sstream>
+
#include "IPAllow.h"
-#include "tscore/BufferWriter.h"
-#include "tscore/ts_file.h"
-#include "tscore/ink_memory.h"
#include "tscore/Filenames.h"
+#include "tscpp/util/ts_errata.h"
-#include "yaml-cpp/yaml.h"
-
-using ts::TextView;
+#include "swoc/Vectray.h"
+#include "swoc/BufferWriter.h"
+#include "swoc/bwf_std.h"
+#include "swoc/bwf_ex.h"
+#include "swoc/bwf_ip.h"
-namespace
-{
-void
-SignalError(ts::BufferWriter &w, bool &flag)
-{
- if (!flag) {
- flag = true;
- }
- Error("%s", w.data());
-}
+#include "yaml-cpp/yaml.h"
-template <typename... Args>
-void
-ParseError(ts::TextView fmt, Args &&... args)
-{
- ts::LocalBufferWriter<1024> w;
- w.printv(fmt, std::forward_as_tuple(args...));
- w.write('\0');
- Warning("%s", w.data());
-}
+using swoc::TextView;
-} // namespace
+using swoc::BufferWriter;
+using swoc::bwf::Spec;
-namespace ts
+namespace swoc
{
BufferWriter &
-bwformat(BufferWriter &w, BWFSpec const &spec, IpAllow const *obj)
+bwformat(BufferWriter &w, Spec const &spec, IpAllow const *obj)
{
return w.print("{}[{}]", obj->MODULE_NAME, obj->get_config_file().c_str());
}
+// This needs to be in namespace "swoc" or "YAML" or ADL doesn't find the overload.
BufferWriter &
-bwformat(BufferWriter &w, BWFSpec const &spec, YAML::Mark const &mark)
+bwformat(BufferWriter &w, Spec const &spec, YAML::Mark const &mark)
{
return w.print("Line {}", mark.line);
}
-BufferWriter &
-bwformat(BufferWriter &w, BWFSpec const &spec, std::error_code const &ec)
-{
- return w.print("[{}:{}]", ec.value(), ec.message());
-}
-
-} // namespace ts
-
-namespace YAML
-{
-template <> struct convert<ts::TextView> {
- static Node
- encode(ts::TextView const &tv)
- {
- Node zret;
- zret = std::string(tv.data(), tv.size());
- return zret;
- }
- static bool
- decode(const Node &node, ts::TextView &tv)
- {
- if (!node.IsScalar()) {
- return false;
- }
- tv.assign(node.Scalar());
- return true;
- }
-};
-
-} // namespace YAML
+} // namespace swoc
enum AclOp {
ACL_OP_ALLOW, ///< Allow access.
@@ -119,6 +76,15 @@ static ConfigUpdateHandler<IpAllow> *ipAllowUpdate;
//
// Begin API functions
//
+swoc::TextView
+IpAllow::localize(swoc::TextView src)
+{
+ auto span = _arena.alloc(src.size() + 1).rebind<char>(); // always make a C-str if copying.
+ memcpy(span.data(), src.data(), src.size());
+ span[src.size()] = '\0';
+ return span.remove_suffix(1); // don't put the extra terminating nul in the view.
+}
+
void
IpAllow::startup()
{
@@ -144,10 +110,12 @@ IpAllow::reconfigure()
Note("%s loading ...", ts::filename::IP_ALLOW);
- new_table = new self_type("proxy.config.cache.ip_allow.filename");
- int retStatus = new_table->BuildTable();
- if (retStatus) {
- Error("%s failed to load", ts::filename::IP_ALLOW);
+ new_table = new self_type("proxy.config.cache.ip_allow.filename");
+ auto errata = new_table->BuildTable();
+ if (!errata.is_ok()) {
+ std::string text;
+ swoc::bwprint(text, "{} failed to load\n{}", ts::filename::IP_ALLOW, errata);
+ Error("%s", text.c_str());
delete new_table;
} else {
configid = configProcessor.set(configid, new_table);
@@ -174,27 +142,30 @@ IpAllow::release()
}
IpAllow::ACL
-IpAllow::match(sockaddr const *ip, match_key_t key)
+IpAllow::match(swoc::IPAddr const &addr, match_key_t key)
{
- self_type *self = acquire();
- void *raw = nullptr;
+ self_type *self = acquire();
+ Record const *record = nullptr;
if (SRC_ADDR == key) {
- self->_src_map.contains(ip, &raw);
- Record *r = static_cast<Record *>(raw);
- // Special check - if checking in accept is enabled and the record is a deny all,
- // then return a missing record instead to force an immediate deny. Otherwise it's delayed
- // until after remap, to allow remap rules to tweak the result.
- if (raw && r->_method_mask == 0 && r->_nonstandard_methods.empty() && accept_check_p) {
- raw = nullptr;
+ if (auto spot = self->_src_map.find(addr); spot != self->_src_map.end()) {
+ auto r = std::get<1>(*spot);
+ // Special case - if checking in accept is enabled and the record is a deny all,
+ // then return a missing record instead to force an immediate deny. Otherwise it's delayed
+ // until after remap, to allow remap rules to tweak the result.
+ if (!(accept_check_p && r->_method_mask == 0 && r->_nonstandard_methods.empty())) {
+ record = r;
+ }
}
- } else {
- self->_dst_map.contains(ip, &raw);
+ } else if (auto spot = self->_dst_map.find(addr); spot != self->_dst_map.end()) {
+ record = std::get<1>(*spot);
}
- if (raw == nullptr) {
- self->release();
- self = nullptr;
+
+ if (record == nullptr) {
+ self->release(); // no record, don't keep a reference to the config.
+ return {};
}
- return ACL{static_cast<Record *>(raw), self};
+
+ return ACL{record, self}; // Note this keeps the config in memory.
}
//
@@ -203,400 +174,272 @@ IpAllow::match(sockaddr const *ip, match_key_t key)
IpAllow::IpAllow(const char *config_var) : config_file(ats_scoped_str(RecConfigReadConfigPath(config_var)).get()) {}
-void
-IpAllow::PrintMap(const IpMap *map) const
+BufferWriter &
+bwformat(BufferWriter &w, Spec const &spec, IpAllow::IpMap const &map)
{
- std::ostringstream s;
- s << map->count() << " ACL entries.";
- for (auto &spot : *map) {
- char text[INET6_ADDRSTRLEN];
- Record const *ar = static_cast<Record const *>(spot.data());
-
- s << std::endl << " Line " << ar->_src_line << ": " << ats_ip_ntop(spot.min(), text, sizeof text);
- if (0 != ats_ip_addr_cmp(spot.min(), spot.max())) {
- s << " - " << ats_ip_ntop(spot.max(), text, sizeof text);
- }
- s << " method=";
- uint32_t mask = ALL_METHOD_MASK & ar->_method_mask;
- if (ALL_METHOD_MASK == mask) {
- s << "ALL";
+ w.print("{} entries", map.count());
+ for (auto const &spot : map) {
+ auto const *r = std::get<1>(spot);
+ w.print("\n Line {}: {} methods=", r->_src_line, std::get<0>(spot));
+ uint32_t mask = IpAllow::ALL_METHOD_MASK & r->_method_mask;
+ if (IpAllow::ALL_METHOD_MASK == mask) {
+ w.write("ALL");
} else if (0 == mask) {
- s << "NONE";
+ w.write("NONE");
} else {
bool leader = false; // need leading vbar?
uint32_t test_mask = 1; // mask for current method.
for (int i = 0; i < HTTP_WKSIDX_METHODS_CNT; ++i, test_mask <<= 1) {
if (mask & test_mask) {
- if (leader) {
- s << '|';
- }
- s << hdrtoken_index_to_wks(i + HTTP_WKSIDX_CONNECT);
+ w.print("{}{}", swoc::bwf::If(leader, "|"), hdrtoken_index_to_wks(i + HTTP_WKSIDX_CONNECT));
leader = true;
}
}
}
- if (!ar->_nonstandard_methods.empty()) {
- s << " other methods=";
+
+ if (!r->_nonstandard_methods.empty()) {
+ w.print(" {}=", r->_deny_nonstandard_methods ? IpAllow::YAML_VALUE_ACTION_ALLOW : IpAllow::YAML_VALUE_ACTION_DENY);
bool leader = false; // need leading vbar?
- for (const auto &_nonstandard_method : ar->_nonstandard_methods) {
- if (leader) {
- s << '|';
- }
- s << _nonstandard_method;
+ for (auto const &name : r->_nonstandard_methods) {
+ w.print("{}{}", swoc::bwf::If(leader, "|"), name);
leader = true;
}
}
}
- Debug("ip_allow", "%s", s.str().c_str());
+ return w;
+}
+
+void
+IpAllow::DebugMap(const IpMap &map) const
+{
+ std::string out;
+ out.resize(8192);
+ swoc::bwprint(out, "{}", map);
+ Debug("ip_allow", "%s", out.c_str());
}
void
IpAllow::Print() const
{
Debug("ip_allow", "Printing src map");
- PrintMap(&_src_map);
+ DebugMap(_src_map);
Debug("ip_allow", "Printing dest map");
- PrintMap(&_dst_map);
+ DebugMap(_dst_map);
}
-int
+swoc::Errata
IpAllow::BuildTable()
{
// Table should be empty
ink_assert(_src_map.count() == 0 && _dst_map.count() == 0);
std::error_code ec;
- std::string content{ts::file::load(config_file, ec)};
+ std::string content{swoc::file::load(config_file, ec)};
+ swoc::Errata errata;
if (ec.value() == 0) {
- // If it's a .yaml or the root tag is present, treat as YAML.
- if (TextView{config_file.view()}.take_suffix_at('.') == "yaml" || std::string::npos != content.find(YAML_TAG_ROOT)) {
- try {
- this->YAMLBuildTable(content);
- } catch (std::exception &ex) {
- ParseError("{} - Invalid config: {}", this, ex.what());
- return 1;
- }
- } else {
- this->ATSBuildTable(content);
+ try {
+ errata = this->YAMLBuildTable(content);
+ } catch (std::exception &ex) {
+ return swoc::Errata(ec, ERRATA_ERROR, "{} - Invalid config: {}", this, ex.what());
+ }
+ if (!errata.is_ok()) {
+ errata.note("While parsing config file");
+ return errata;
}
if (_src_map.count() == 0 && _dst_map.count() == 0) {
- ParseError("{} - No entries found. All IP Addresses will be blocked", this);
- return 1;
+ return swoc::Errata(ERRATA_ERROR, "{} - No entries found. All IP Addresses will be blocked", this);
}
- // convert the coloring from indices to pointers.
- for (auto &item : _src_map) {
- item.setData(&_src_acls[reinterpret_cast<size_t>(item.data())]);
- }
- for (auto &item : _dst_map) {
- item.setData(&_dst_acls[reinterpret_cast<size_t>(item.data())]);
- }
if (is_debug_tag_set("ip_allow")) {
Print();
}
} else {
- ParseError("{} Failed to load {}. All IP Addresses will be blocked", this, ec);
- return 1;
+ return swoc::Errata(ERRATA_ERROR, "{} Failed to load {}. All IP Addresses will be blocked", this, ec);
}
- return 0;
+ return {};
}
-bool
+swoc::Errata
IpAllow::YAMLLoadMethod(const YAML::Node &node, Record &rec)
{
- const std::string &value{node.Scalar()};
+ swoc::TextView value{node.Scalar()};
+ swoc::Vectray<swoc::TextView, 8> names;
+ // Process a single token. Required to deal with the variable number of tokens.
+ auto parse_method = [&](swoc::TextView value) -> void {
+ if (0 == strcasecmp(value, YAML_VALUE_METHODS_ALL)) {
+ rec._method_mask = ALL_METHOD_MASK;
+ } else {
+ int method_idx = hdrtoken_tokenize(value.data(), value.size());
+ if (HTTP_WKSIDX_CONNECT <= method_idx && method_idx < HTTP_WKSIDX_CONNECT + HTTP_WKSIDX_METHODS_CNT) {
+ rec._method_mask |= ACL::MethodIdxToMask(method_idx);
+ } else {
+ names.push_back(value);
+ Debug("ip_allow", "Found nonstandard method '%.*s' at line %d", int(value.size()), value.data(), node.Mark().line);
+ }
+ }
+ };
- if (0 == strcasecmp(value, YAML_VALUE_METHODS_ALL)) {
- rec._method_mask = ALL_METHOD_MASK;
+ if (node.IsScalar()) {
+ parse_method(swoc::TextView(node.Scalar()));
+ } else if (node.IsSequence()) {
+ for (auto const &elt : node) {
+ if (elt.IsScalar()) {
+ parse_method(swoc::TextView(elt.Scalar()));
+ if (rec._method_mask == ALL_METHOD_MASK) {
+ break; // we're done here, nothing else matters.
+ }
+ } else {
+ return swoc::Errata(ERRATA_ERROR, "{} {} - item ignored, all values for '{}' must be strings.", this, elt.Mark(),
+ YAML_TAG_METHODS);
+ }
+ }
} else {
- int method_idx = hdrtoken_tokenize(value.data(), value.size());
- if (method_idx < HTTP_WKSIDX_CONNECT || method_idx >= HTTP_WKSIDX_CONNECT + HTTP_WKSIDX_METHODS_CNT) {
- rec._nonstandard_methods.push_back(value);
- Debug("ip_allow", "Found nonstandard method '%s' at line %d", value.c_str(), node.Mark().line);
- } else { // valid method.
- rec._method_mask |= ACL::MethodIdxToMask(method_idx);
+ return swoc::Errata(ERRATA_ERROR, "{} {} - item ignored, value for '{}' must be a single string or a list of strings.", this,
+ node.Mark(), YAML_TAG_METHODS);
+ }
+
+ // copy over to local memory if it's not all methods and there are non-standard ones.
+ if (rec._method_mask != ALL_METHOD_MASK && !names.empty()) {
+ rec._nonstandard_methods = _arena.alloc_span<swoc::TextView>(names.size());
+ for (unsigned idx = 0; idx < names.size(); ++idx) {
+ rec._nonstandard_methods[idx] = this->localize(names[idx]);
}
}
- return true;
+ return {};
}
-bool
-IpAllow::YAMLLoadIPAddrRange(const YAML::Node &node, IpMap *map, void *mark)
+swoc::Errata
+IpAllow::YAMLLoadIPAddrRange(const YAML::Node &node, IpMap *map, IpAllow::Record const *record)
{
- if (node.IsScalar()) {
- IpAddr min, max;
- if (0 == ats_ip_range_parse(node.Scalar(), min, max)) {
- map->fill(min, max, mark);
- return true;
- } else {
- ParseError("{} {} - '{}' is not a valid range.", this, node.Mark(), node.Scalar());
- }
+ if (!node.IsScalar()) {
+ return swoc::Errata(ERRATA_ERROR, "{} Expected IP address range at {}, found non-literal.", this, node.Mark());
+ }
+
+ swoc::TextView debug(node.Scalar());
+ (void)debug;
+ if (swoc::IPRange r; r.load(node.Scalar())) {
+ map->fill(r, record);
+ } else {
+ return swoc::Errata(ERRATA_ERROR, "{} {} - '{}' is not a valid range.", this, node.Mark(), node.Scalar());
}
- return false;
+ return {};
}
-bool
+swoc::Errata
IpAllow::YAMLLoadEntry(const YAML::Node &entry)
{
AclOp op = ACL_OP_DENY; // "shut up", I explained to the compiler.
YAML::Node node;
- IpAddr min, max;
- std::string value;
- Record rec;
- std::vector<Record> *acls{nullptr};
- IpMap *map = nullptr;
+ auto record = _arena.make<Record>();
+ IpMap *map = nullptr; // src or dst map.
if (!entry.IsMap()) {
- ParseError("{} {} - ACL items must be maps.", this, entry.Mark());
- return false;
+ return swoc::Errata(ERRATA_ERROR, "{} {} - ACL items must be maps.", this, entry.Mark());
}
- if (entry[YAML_TAG_APPLY]) {
- auto apply_node{entry[YAML_TAG_APPLY]};
+ if (YAML::Node apply_node{entry[YAML_TAG_APPLY]}; apply_node) {
if (apply_node.IsScalar()) {
- ts::TextView value{apply_node.Scalar()};
+ swoc::TextView value{apply_node.Scalar()};
if (0 == strcasecmp(value, YAML_VALUE_APPLY_IN)) {
- acls = &_src_acls;
- map = &_src_map;
+ map = &_src_map;
} else if (0 == strcasecmp(value, YAML_VALUE_APPLY_OUT)) {
- acls = &_dst_acls;
- map = &_dst_map;
+ map = &_dst_map;
} else {
- ParseError(R"("{}" value at {} must be "{}" or "{}")", YAML_TAG_APPLY, entry.Mark(), YAML_VALUE_APPLY_IN,
- YAML_VALUE_APPLY_OUT);
- return false;
+ return swoc::Errata(ERRATA_ERROR, R"("{}" value at {} must be "{}" or "{}")", YAML_TAG_APPLY, entry.Mark(),
+ YAML_VALUE_APPLY_IN, YAML_VALUE_APPLY_OUT);
}
} else {
- ParseError(R"("{}" value at {} must be a scalar, "{}" or "{}")", YAML_TAG_APPLY, entry.Mark(), YAML_VALUE_APPLY_IN,
- YAML_VALUE_APPLY_OUT);
- return false;
+ return swoc::Errata(ERRATA_ERROR, R"("{}" value at {} must be a scalar, "{}" or "{}")", YAML_TAG_APPLY, entry.Mark(),
+ YAML_VALUE_APPLY_IN, YAML_VALUE_APPLY_OUT);
}
} else {
- ParseError(R"("Object at {} must have a "{}" key.)", entry.Mark(), YAML_TAG_APPLY);
- return false;
+ return swoc::Errata(ERRATA_ERROR, R"("Object at {} must have a "{}" key.)", entry.Mark(), YAML_TAG_APPLY);
}
- void *ipmap_mark = reinterpret_cast<void *>(acls->size());
- if (entry[YAML_TAG_IP_ADDRS]) {
- auto addr_node{entry[YAML_TAG_IP_ADDRS]};
- if (addr_node.IsSequence()) {
- for (auto const &n : addr_node) {
- if (!this->YAMLLoadIPAddrRange(n, map, ipmap_mark)) {
- return false;
- }
+ if (node = entry[YAML_TAG_ACTION]; node) {
+ if (node.IsScalar()) {
+ swoc::TextView value(node.Scalar());
+ if (value == YAML_VALUE_ACTION_ALLOW) {
+ op = ACL_OP_ALLOW;
+ } else if (value == YAML_VALUE_ACTION_DENY) {
+ op = ACL_OP_DENY;
+ } else {
+ return swoc::Errata(ERRATA_ERROR, "{} {} - item ignored, value for tag '{}' must be '{}' or '{}'", this, node.Mark(),
+ YAML_TAG_ACTION, YAML_VALUE_ACTION_ALLOW, YAML_VALUE_ACTION_DENY);
}
- } else if (!this->YAMLLoadIPAddrRange(addr_node, map, ipmap_mark)) {
- return false;
+ } else {
+ return swoc::Errata(ERRATA_ERROR, "{} {} - item ignored, value for tag '{}' must be a string", this, node.Mark(),
+ YAML_TAG_ACTION);
}
- }
-
- if (!entry[YAML_TAG_ACTION]) {
- ParseError("{} {} - item ignored, required '{}' key not found.", this, entry.Mark(), YAML_TAG_ACTION);
- return false;
- }
-
- node = entry[YAML_TAG_ACTION];
- if (!node.IsScalar()) {
- ParseError("{} {} - item ignored, value for tag '{}' must be a string", this, node.Mark(), YAML_TAG_ACTION);
- return false;
- }
- value = node.as<std::string>();
- if (value == YAML_VALUE_ACTION_ALLOW) {
- op = ACL_OP_ALLOW;
- } else if (value == YAML_VALUE_ACTION_DENY) {
- op = ACL_OP_DENY;
} else {
- ParseError("{} {} - item ignored, value for tag '{}' must be '{}' or '{}'", this, node.Mark(), YAML_TAG_ACTION,
- YAML_VALUE_ACTION_ALLOW, YAML_VALUE_ACTION_DENY);
- return false;
+ return swoc::Errata(ERRATA_ERROR, "{} {} - item ignored, required '{}' key not found.", this, entry.Mark(), YAML_TAG_ACTION);
}
- if (!entry[YAML_TAG_METHODS]) {
- rec._method_mask = ALL_METHOD_MASK;
- } else {
- node = entry[YAML_TAG_METHODS];
- if (node.IsScalar()) {
- this->YAMLLoadMethod(node, rec);
- } else if (node.IsSequence()) {
- for (auto const &elt : node) {
- if (elt.IsScalar()) {
- this->YAMLLoadMethod(elt, rec);
- if (rec._method_mask == ALL_METHOD_MASK) {
- break; // we're done here, nothing else matters.
- }
+
+ if (YAML::Node addr_node = entry[YAML_TAG_IP_ADDRS]; addr_node) {
+ bool marked_p = false;
+ if (addr_node.IsSequence()) {
+ for (auto const &n : addr_node) {
+ if (auto errata = this->YAMLLoadIPAddrRange(n, map, record); errata.is_ok()) {
+ marked_p = true;
} else {
- ParseError("{} {} - item ignored, all values for '{}' must be strings.", this, elt.Mark(), YAML_TAG_METHODS);
- return false;
+ errata.note(R"(In record at {})", entry.Mark());
+ return errata;
}
}
} else {
- ParseError("{} {} - item ignored, value for '{}' must be a single string or a list of strings.", this, node.Mark(),
- YAML_TAG_METHODS);
+ if (auto errata = this->YAMLLoadIPAddrRange(addr_node, map, record); errata.is_ok()) {
+ marked_p = true;
+ } else {
+ errata.note(R"(In record at {})", entry.Mark());
+ return errata;
+ }
+ }
+ if (!marked_p) {
+ return swoc::Errata(ERRATA_ERROR, "No valid addresses for rule at {}", node.Mark());
}
+ } else {
+ return swoc::Errata(ERRATA_ERROR, "{} {} - item ignored, required '{}' key not found.", this, entry.Mark(), YAML_TAG_IP_ADDRS);
}
+
+ if (node = entry[YAML_TAG_METHODS]; node) {
+ if (auto errata = this->YAMLLoadMethod(node, *record); !errata.is_ok()) {
+ return errata;
+ }
+ } else {
+ record->_method_mask = ALL_METHOD_MASK;
+ }
+
if (op == ACL_OP_DENY) {
- rec._method_mask = ALL_METHOD_MASK & ~rec._method_mask;
- rec._deny_nonstandard_methods = true;
+ record->_method_mask = ALL_METHOD_MASK & ~record->_method_mask;
+ record->_deny_nonstandard_methods = true;
}
- rec._src_line = entry.Mark().line;
- // If we get here, everything parsed OK, add the record.
- acls->emplace_back(std::move(rec));
- return true;
+
+ record->_src_line = entry.Mark().line;
+ return {};
}
-int
+swoc::Errata
IpAllow::YAMLBuildTable(std::string const &content)
{
YAML::Node root{YAML::Load(content)};
if (!root.IsMap()) {
- ParseError("{} - top level object was not a map. All IP Addresses will be blocked", this);
- return 1;
+ return swoc::Errata("{} - top level object was not a map. All IP Addresses will be blocked", this);
}
- YAML::Node data{root[YAML_TAG_ROOT]};
+ YAML::Node data{root[YAML_TAG_ROOT.data()]};
if (!data) {
- ParseError("{} - root tag '{}' not found. All IP Addresses will be blocked", this, YAML_TAG_ROOT);
+ return swoc::Errata("{} - root tag '{}' not found. All IP Addresses will be blocked", this, YAML_TAG_ROOT);
} else if (data.IsSequence()) {
for (auto const &entry : data) {
- if (!this->YAMLLoadEntry(entry)) {
- return 1;
+ if (auto errata = this->YAMLLoadEntry(entry); !errata.is_ok()) {
+ return errata;
}
}
} else if (data.IsMap()) {
- this->YAMLLoadEntry(data); // singleton, just load it.
+ return this->YAMLLoadEntry(data); // singleton, just load it.
} else {
- ParseError("{} - root tag '{}' is not an map or sequence. All IP Addresses will be blocked", this, YAML_TAG_ROOT);
- return 1;
- }
- return 0;
-}
-
-int
-IpAllow::ATSBuildTable(std::string const &content)
-{
- int line_num = 0;
- IpAddr addr1;
- IpAddr addr2;
- bool alarmAlready = false;
- ts::LocalBufferWriter<1024> bw_err;
-
- TextView src(content);
- TextView line;
- auto err_prefix = [&]() -> ts::BufferWriter & {
- return bw_err.reset().print("{} discarding '{}' entry at line {} : ", MODULE_NAME, config_file.c_str(), line_num);
- };
-
- while (!(line = src.take_prefix_at('\n')).empty()) {
- ++line_num;
- line.trim_if(&isspace);
-
- if (!line.empty() && *line != '#') {
- TextView token = line.take_prefix_if(&isspace);
- TextView value = token.split_suffix_at('=');
- match_key_t match;
- if (value.empty()) {
- err_prefix().print("No value found in token '{}'.\0", token);
- SignalError(bw_err, alarmAlready);
- continue;
- } else if (strcasecmp(token, OPT_MATCH_SRC) == 0) {
- match = SRC_ADDR;
- } else if (strcasecmp(token, OPT_MATCH_DST) == 0) {
- match = DST_ADDR;
- } else {
- err_prefix().print("'{}' is not a valid key.\0", token);
- SignalError(bw_err, alarmAlready);
- continue;
- }
-
- if (0 == ats_ip_range_parse(value, addr1, addr2)) {
- uint32_t acl_method_mask = 0;
- bool op_found_p = false;
- bool method_found_p = false;
- bool all_found_p = false;
- bool deny_nonstandard_methods = false;
- bool line_valid_p = true;
- AclOp op = ACL_OP_DENY; // "shut up", I explained to the compiler.
- MethodNames nonstandard_methods;
-
- while (line_valid_p && !line.ltrim_if(&isspace).empty()) {
- token = line.take_prefix_if(&isspace);
- value = token.split_suffix_at('=');
-
- if (value.empty()) {
- err_prefix().print("No value found in token '{}'\0", token);
- SignalError(bw_err, alarmAlready);
- line_valid_p = false;
- } else if (strcasecmp(token, OPT_ACTION_TAG) == 0) {
- if (strcasecmp(value, OPT_ACTION_ALLOW) == 0) {
- op_found_p = true, op = ACL_OP_ALLOW;
- } else if (strcasecmp(value, OPT_ACTION_DENY) == 0) {
- op_found_p = true, op = ACL_OP_DENY;
- } else {
- err_prefix().print("'{}' is not a valid action\0", value);
- SignalError(bw_err, alarmAlready);
- line_valid_p = false;
- }
- } else if (strcasecmp(token, OPT_METHOD) == 0) {
- // Parse method="GET|HEAD"
- while (!value.empty()) {
- TextView method_name = value.take_prefix_at('|');
- if (strcasecmp(method_name, OPT_METHOD_ALL) == 0) {
- all_found_p = true;
- break;
- } else {
- int method_idx = hdrtoken_tokenize(method_name.data(), method_name.size());
- if (method_idx < HTTP_WKSIDX_CONNECT || method_idx >= HTTP_WKSIDX_CONNECT + HTTP_WKSIDX_METHODS_CNT) {
- nonstandard_methods.emplace_back(std::string(method_name.data(), method_name.size()));
- Debug("ip_allow", "%s",
- bw_err.reset().print("Found nonstandard method '{}' on line {}\0", method_name, line_num).data());
- } else { // valid method.
- acl_method_mask |= ACL::MethodIdxToMask(method_idx);
- }
- method_found_p = true;
- }
- }
- } else {
- err_prefix().print("'{}' is not a valid token\0", token);
- SignalError(bw_err, alarmAlready);
- line_valid_p = false;
- }
- }
- if (!line_valid_p) {
- continue; // error parsing the line, go on to the next.
- }
- if (!op_found_p) {
- err_prefix().print("No action found.\0");
- SignalError(bw_err, alarmAlready);
- continue;
- }
- // If method not specified, default to ALL
- if (all_found_p || !method_found_p) {
- method_found_p = true;
- acl_method_mask = ALL_METHOD_MASK;
- nonstandard_methods.clear();
- }
- // When deny, use bitwise complement. (Make the rule 'allow for all
- // methods except those specified')
- if (op == ACL_OP_DENY) {
- acl_method_mask = ALL_METHOD_MASK & ~acl_method_mask;
- deny_nonstandard_methods = true;
- }
-
- if (method_found_p) {
- std::vector<Record> &acls = match == DST_ADDR ? _dst_acls : _src_acls;
- IpMap &map = match == DST_ADDR ? _dst_map : _src_map;
- acls.emplace_back(acl_method_mask, line_num, std::move(nonstandard_methods), deny_nonstandard_methods);
- // Color with index in acls because at this point the address is volatile.
- map.fill(addr1, addr2, reinterpret_cast<void *>(acls.size() - 1));
- } else {
- err_prefix().print("No valid method found\0"); // changed by YTS Team, yamsat bug id -59022
- SignalError(bw_err, alarmAlready);
- }
- } else {
- err_prefix().print("'{}' is not a valid IP address range\0", value);
- SignalError(bw_err, alarmAlready);
- }
- }
+ return swoc::Errata("{} - root tag '{}' is not an map or sequence. All IP Addresses will be blocked", this, YAML_TAG_ROOT);
}
- return 0;
+ return {};
}
diff --git a/proxy/IPAllow.h b/proxy/IPAllow.h
index 9221e7525..4dd9053fa 100644
--- a/proxy/IPAllow.h
+++ b/proxy/IPAllow.h
@@ -36,9 +36,10 @@
#include "hdrs/HTTP.h"
#include "ProxyConfig.h"
-#include "tscore/IpMap.h"
-#include "tscpp/util/TextView.h"
-#include "tscore/ts_file.h"
+#include "swoc/TextView.h"
+#include "swoc/swoc_file.h"
+#include "swoc/swoc_ip.h"
+#include "swoc/Errata.h"
// forward declare in name only so it can be a friend.
struct IpAllowUpdate;
@@ -53,7 +54,7 @@ class IpAllow : public ConfigInfo
{
friend struct IpAllowUpdate;
- using MethodNames = std::vector<std::string>;
+ using MethodNames = swoc::MemSpan<swoc::TextView>;
static constexpr uint32_t ALL_METHOD_MASK = ~0; // Mask for all methods.
@@ -65,40 +66,54 @@ class IpAllow : public ConfigInfo
/// Present only to make Vec<> happy, do not use.
Record() = default;
Record(Record &&that) = default;
+ /** Construct from mask.
+ *
+ * @param method_mask Bit mask of allowed methods.
+ */
explicit Record(uint32_t method_mask);
+
+ /** Construct from values.
+ *
+ * @param method_mask Well known method mask.
+ * @param line Source line in configuration file.
+ * @param nonstandard_methods Allowed methods that are not well known.
+ * @param deny_nonstandard_methods Denied methods that are not well known.
+ */
Record(uint32_t method_mask, int line, MethodNames &&nonstandard_methods, bool deny_nonstandard_methods);
- uint32_t _method_mask{0};
- int _src_line{0};
- MethodNames _nonstandard_methods;
- bool _deny_nonstandard_methods{false};
+ uint32_t _method_mask{0}; ///< Well known method mask.
+ int _src_line{0}; ///< Configuration file sourc line.
+ MethodNames _nonstandard_methods; ///< Allowed methods that are not well known.
+ bool _deny_nonstandard_methods{false}; ///< Denied methods that are not well known.
};
public:
using self_type = IpAllow; ///< Self reference type.
using scoped_config = ConfigProcessor::scoped_config<self_type, self_type>;
+ using IpMap = swoc::IPSpace<Record const *>;
// indicator for whether we should be checking the acl record for src ip or dest ip
enum match_key_t { SRC_ADDR, DST_ADDR };
+
/// Token strings for configuration
- static constexpr ts::TextView OPT_MATCH_SRC{"src_ip"};
- static constexpr ts::TextView OPT_MATCH_DST{"dest_ip"};
- static constexpr ts::TextView OPT_ACTION_TAG{"action"};
- static constexpr ts::TextView OPT_ACTION_ALLOW{"ip_allow"};
- static constexpr ts::TextView OPT_ACTION_DENY{"ip_deny"};
- static constexpr ts::TextView OPT_METHOD{"method"};
- static constexpr ts::TextView OPT_METHOD_ALL{"all"};
-
- static constexpr ts::TextView YAML_TAG_ROOT{"ip_allow"};
- static constexpr ts::TextView YAML_TAG_IP_ADDRS{"ip_addrs"};
- static constexpr ts::TextView YAML_TAG_APPLY{"apply"};
- static constexpr ts::TextView YAML_VALUE_APPLY_IN{"in"};
- static constexpr ts::TextView YAML_VALUE_APPLY_OUT{"out"};
- static constexpr ts::TextView YAML_TAG_ACTION{"action"};
- static constexpr ts::TextView YAML_VALUE_ACTION_ALLOW{"allow"};
- static constexpr ts::TextView YAML_VALUE_ACTION_DENY{"deny"};
- static constexpr ts::TextView YAML_TAG_METHODS{"methods"};
- static constexpr ts::TextView YAML_VALUE_METHODS_ALL{"all"};
+ static constexpr swoc::TextView OPT_MATCH_SRC{"src_ip"};
+ static constexpr swoc::TextView OPT_MATCH_DST{"dest_ip"};
+ static constexpr swoc::TextView OPT_ACTION_TAG{"action"};
+ static constexpr swoc::TextView OPT_ACTION_ALLOW{"ip_allow"};
+ static constexpr swoc::TextView OPT_ACTION_DENY{"ip_deny"};
+ static constexpr swoc::TextView OPT_METHOD{"method"};
+ static constexpr swoc::TextView OPT_METHOD_ALL{"all"};
+
+ static const inline std::string YAML_TAG_ROOT{"ip_allow"};
+ static const inline std::string YAML_TAG_IP_ADDRS{"ip_addrs"};
+ static const inline std::string YAML_TAG_APPLY{"apply"};
+ static const inline std::string YAML_VALUE_APPLY_IN{"in"};
+ static const inline std::string YAML_VALUE_APPLY_OUT{"out"};
+ static const inline std::string YAML_TAG_ACTION{"action"};
+ static const inline std::string YAML_VALUE_ACTION_ALLOW{"allow"};
+ static const inline std::string YAML_VALUE_ACTION_DENY{"deny"};
+ static const inline std::string YAML_TAG_METHODS{"methods"};
+ static const inline std::string YAML_VALUE_METHODS_ALL{"all"};
static constexpr const char *MODULE_NAME = "IPAllow";
@@ -121,6 +136,11 @@ public:
void clear(); ///< Drop data and config reference.
+ /** Convert well known string index to mask.
+ *
+ * @param wksidx Well known string index.
+ * @return A mask for that method.
+ */
static uint32_t MethodIdxToMask(int wksidx);
/// Check if the ACL is valid (i.e. not uninitialized or missing).
@@ -149,8 +169,9 @@ public:
void Print() const;
- static ACL match(sockaddr const *ip, match_key_t key);
- static ACL match(IpEndpoint const *ip, match_key_t key);
+ static ACL match(swoc::IPAddr const &addr, match_key_t key);
+ static ACL match(swoc::IPEndpoint const *addr, match_key_t key);
+ static ACL match(sockaddr const *sa, match_key_t key);
static void startup();
static void reconfigure();
@@ -180,27 +201,31 @@ public:
*/
static bool isAcceptCheckEnabled();
- const ts::file::path &get_config_file() const;
+ const swoc::file::path &get_config_file() const;
private:
static size_t configid; ///< Configuration ID for update management.
static const Record ALLOW_ALL_RECORD; ///< Static record that allows all access.
static bool accept_check_p; ///< @c true if deny all can be enforced during accept.
- void PrintMap(const IpMap *map) const;
+ void DebugMap(IpMap const &map) const;
- int BuildTable();
- int ATSBuildTable(const std::string &);
- int YAMLBuildTable(const std::string &);
- bool YAMLLoadEntry(const YAML::Node &);
- bool YAMLLoadIPAddrRange(const YAML::Node &, IpMap *map, void *mark);
- bool YAMLLoadMethod(const YAML::Node &node, Record &rec);
+ swoc::Errata BuildTable();
+ swoc::Errata YAMLBuildTable(const std::string &);
+ swoc::Errata YAMLLoadEntry(const YAML::Node &);
+ swoc::Errata YAMLLoadIPAddrRange(const YAML::Node &, IpMap *map, Record const *mark);
+ swoc::Errata YAMLLoadMethod(const YAML::Node &node, Record &rec);
- ts::file::path config_file; ///< Path to configuration file.
+ /// Copy @a src to the local arena and review a view of the copy.
+ swoc::TextView localize(swoc::TextView src);
+
+ swoc::file::path config_file; ///< Path to configuration file.
IpMap _src_map;
IpMap _dst_map;
- std::vector<Record> _src_acls;
- std::vector<Record> _dst_acls;
+ /// Storage for records.
+ swoc::MemArena _arena;
+
+ friend swoc::BufferWriter &bwformat(swoc::BufferWriter &w, swoc::bwf::Spec const &spec, IpAllow::IpMap const &map);
};
// ------ Record methods --------
@@ -321,9 +346,15 @@ IpAllow::isAcceptCheckEnabled()
}
inline auto
-IpAllow::match(IpEndpoint const *ip, match_key_t key) -> ACL
+IpAllow::match(swoc::IPEndpoint const *addr, match_key_t key) -> ACL
+{
+ return self_type::match(swoc::IPAddr(&addr->sa), key);
+}
+
+inline auto
+IpAllow::match(sockaddr const *sa, match_key_t key) -> ACL
{
- return self_type::match(&ip->sa, key);
+ return self_type::match(swoc::IPAddr(sa), key);
}
inline auto
@@ -332,7 +363,7 @@ IpAllow::makeAllowAllACL() -> ACL
return {&ALLOW_ALL_RECORD, nullptr};
}
-inline const ts::file::path &
+inline const swoc::file::path &
IpAllow::get_config_file() const
{
return config_file;