return false; // no more tokens (i.e. we are at the end of the line)
}
-Acl::Tree *
+ACLList *
ConfigParser::optionalAclList()
{
if (!skipOptional("if"))
return nullptr; // OK: the directive has no ACLs
- Acl::Tree *acls = nullptr;
+ ACLList *acls = nullptr;
const auto aclCount = aclParseAclList(*this, &acls, cfg_directive);
assert(acls);
if (aclCount <= 0)
bool skipOptional(const char *keyword);
/// parses an [if [!]<acl>...] construct
- Acl::Tree *optionalAclList();
+ ACLList *optionalAclList();
/// extracts and returns a regex (including any optional flags)
std::unique_ptr<RegexPattern> regex(const char *expectedRegexDescription);
os << directiveName << ' ' << key() << ' ' << ConfigParser::QuoteString(SBufToString(v->value()));
if (v->aclList) {
// TODO: Use Acl::dump() after fixing the XXX in dump_acl_list().
- for (const auto &item: v->aclList->treeDump("", &Acl::AllowOrDeny)) {
+ for (const auto &item: ToTree(v->aclList).treeDump("", &Acl::AllowOrDeny)) {
if (item.isEmpty()) // treeDump("") adds this prefix
continue;
if (item.cmp("\n") == 0) // treeDump() adds this suffix
std::chrono::nanoseconds paranoid_hit_validation;
- class Acl::Node *aclList;
+ Acl::NamedAcls *namedAcls; ///< acl aclname acltype ...
struct {
acl_access *http;
#include "debug/Stream.h"
#include "fatal.h"
#include "globals.h"
+#include "mem/PoolingAllocator.h"
+#include "sbuf/Algorithms.h"
#include "sbuf/List.h"
#include "sbuf/Stream.h"
#include "SquidConfig.h"
#include <algorithm>
#include <map>
+#include <unordered_map>
namespace Acl {
+/// parsed "acl aclname ..." directives indexed by aclname
+class NamedAcls: public std::unordered_map<SBuf, Acl::Node::Pointer,
+ CaseInsensitiveSBufHash, CaseInsensitiveSBufEqual,
+ PoolingAllocator< std::pair<const SBuf, Acl::Node::Pointer> > > {
+};
+
/// Acl::Node type name comparison functor
class TypeNameCmp {
public:
fatal ("unusable Acl::Node::delete");
}
-/// implements both Acl::Node::FindByName() variations
-template <typename SBufOrCString>
-static Acl::Node *
-FindByNameT(const SBufOrCString name)
+Acl::Node *
+Acl::Node::FindByName(const SBuf &name)
{
- for (auto a = Config.aclList; a; a = a->next) {
- if (a->name.caseCmp(name) == 0) {
- debugs(28, 8, "found " << a->name);
- return a;
- }
+ if (!Config.namedAcls) {
+ debugs(28, 8, "no named ACLs to find " << name);
+ return nullptr;
}
- debugs(28, 8, "cannot find " << name);
- return nullptr;
-}
+ const auto result = Config.namedAcls->find(name);
+ if (result == Config.namedAcls->end()) {
+ debugs(28, 8, "no ACL named " << name);
+ return nullptr;
+ }
-Acl::Node *
-Acl::Node::FindByName(const SBuf &name)
-{
- return FindByNameT(name);
+ debugs(28, 8, result->second << " is named " << name);
+ assert(result->second);
+ return result->second.getRaw();
}
-Acl::Node *
-Acl::Node::FindByName(const char *name)
+Acl::Node::Node()
{
- return FindByNameT(name);
+ debugs(28, 8, "constructing, this=" << this);
}
-Acl::Node::Node() :
- cfgline(nullptr),
- next(nullptr),
- registered(false)
-{}
-
bool
Acl::Node::valid() const
{
}
void
-Acl::Node::ParseAclLine(ConfigParser &parser, Node ** head)
+Acl::Node::ParseNamedAcl(ConfigParser &parser, NamedAcls *&namedAcls)
{
/* we're already using strtok() to grok the line */
char *t = nullptr;
return;
}
+ if (!namedAcls)
+ namedAcls = new NamedAcls();
+
SBuf aclname(t);
CallParser(ParsingContext::Pointer::Make(aclname), [&] {
- ParseNamed(parser, head, aclname);
+ ParseNamed(parser, *namedAcls, aclname);
});
}
/// parses acl directive parts that follow aclname
void
-Acl::Node::ParseNamed(ConfigParser &parser, Node ** const head, const SBuf &aclname)
+Acl::Node::ParseNamed(ConfigParser &parser, NamedAcls &namedAcls, const SBuf &aclname)
{
/* snarf the ACL type */
const char *theType;
theType = "client_connection_mark";
}
- Node *A = nullptr;
+ auto A = FindByName(aclname);
int new_acl = 0;
- if ((A = FindByName(aclname)) == nullptr) {
+ if (!A) {
debugs(28, 3, "aclParseAclLine: Creating ACL '" << aclname << "'");
A = Acl::Make(theType);
A->context(aclname, config_input_line);
A->cfgline);
}
- // add to the global list for searching explicit ACLs by name
- assert(head && *head == Config.aclList);
- A->next = *head;
- *head = A;
+ const auto insertion = namedAcls.emplace(A->name, A);
+ Assure(insertion.second); // FindByName() above checked that A is a new ACL
+}
- // register for centralized cleanup
- aclRegister(A);
+void
+Acl::DumpNamedAcls(std::ostream &os, const char * const directiveName, NamedAcls * const namedAcls)
+{
+ if (namedAcls) {
+ for (const auto &nameAndAcl: *namedAcls) {
+ debugs(3, 3, directiveName << ' ' << nameAndAcl.first);
+ nameAndAcl.second->dumpWhole(directiveName, os);
+ }
+ }
+}
+
+void
+Acl::FreeNamedAcls(NamedAcls ** const namedAcls)
+{
+ assert(namedAcls);
+ delete *namedAcls;
+ *namedAcls = nullptr;
}
bool
Acl::Node::~Node()
{
- debugs(28, 3, "freeing ACL " << name);
+ debugs(28, 8, "destructing " << name << ", this=" << this);
safe_free(cfgline);
}
void
Acl::Node::Initialize()
{
- auto *a = Config.aclList;
- debugs(53, 3, "Acl::Node::Initialize");
-
- while (a) {
- a->prepareForUse();
- a = a->next;
+ debugs(28, 3, (Config.namedAcls ? Config.namedAcls->size() : 0));
+ if (Config.namedAcls) {
+ for (const auto &nameAndAcl: *Config.namedAcls)
+ nameAndAcl.second->prepareForUse();
}
}
return o;
}
+/// report the given list of "acl" directives (using squid.conf syntax)
+void DumpNamedAcls(std::ostream &, const char *directiveName, NamedAcls *);
+
+/// delete the given list of "acl" directives
+void FreeNamedAcls(NamedAcls **);
+
} // namespace Acl
/// \ingroup ACLAPI
#include "acl/AllOf.h"
#include "acl/BoolOps.h"
#include "acl/Checklist.h"
-#include "acl/Gadgets.h"
#include "cache_cf.h"
#include "MemBuf.h"
#include "sbuf/SBuf.h"
if (empty())
return 1; // not 0 because in math empty product equals identity
- if (checklist->matchChild(this, start, *start))
+ if (checklist->matchChild(this, start))
return 1; // match
return checklist->keepMatching() ? 0 : -1;
Acl::AllOf::parse()
{
Acl::InnerNode *whole = nullptr;
- Acl::Node *oldNode = empty() ? nullptr : nodes.front();
+ const auto oldNode = empty() ? nullptr : nodes.front().getRaw();
// optimization: this logic reduces subtree hight (number of tree levels)
if (Acl::OrNode *oldWhole = dynamic_cast<Acl::OrNode*>(oldNode)) {
newWhole->context(ToSBuf('(', name, " lines)"), oldNode->cfgline);
newWhole->add(oldNode); // old (i.e. first) line
nodes.front() = whole = newWhole;
- aclRegister(newWhole);
} else {
// this is the first line for this acl; just use it as is
whole = this;
{
assert(start == nodes.begin()); // we only have one node
- if (checklist->matchChild(this, start, *start))
+ if (checklist->matchChild(this, start))
return 0; // converting match into mismatch
if (!checklist->keepMatching())
{
// find the first node that does not match
for (Nodes::const_iterator i = start; i != nodes.end(); ++i) {
- if (!checklist->matchChild(this, i, *i))
+ if (!checklist->matchChild(this, i))
return checklist->keepMatching() ? 0 : -1;
}
for (Nodes::const_iterator i = start; i != nodes.end(); ++i) {
if (bannedAction(checklist, i))
continue;
- if (checklist->matchChild(this, i, *i)) {
+ if (checklist->matchChild(this, i)) {
lastMatch_ = i;
return 1;
}
return false;
}
- /** \par
- * If the accessList is no longer valid (i.e. its been
- * freed because of a reconfigure), then bail with ACCESS_DUNNO.
- */
-
- if (!cbdataReferenceValid(accessList)) {
- cbdataReferenceDone(accessList);
- checkCallback("accessList is invalid");
- return false;
- }
-
return true;
}
if (!finished())
calcImplicitAnswer();
- cbdataReferenceDone(accessList);
checkCallback(nullptr);
}
}
bool
-ACLChecklist::matchChild(const Acl::InnerNode *current, Acl::Nodes::const_iterator pos, const Acl::Node *child)
+ACLChecklist::matchChild(const Acl::InnerNode * const current, const Acl::Nodes::const_iterator pos)
{
+ const auto &child = *pos;
assert(current && child);
// Remember the current tree location to prevent "async loop" cases where
ACLChecklist::~ACLChecklist()
{
assert (!asyncInProgress());
+ debugs(28, 4, "ACLChecklist::~ACLChecklist: destroyed " << this);
+}
- changeAcl(nullptr);
+void
+ACLChecklist::changeAcl(const acl_access * const replacement)
+{
+ accessList = replacement ? *replacement : nullptr;
+}
- debugs(28, 4, "ACLChecklist::~ACLChecklist: destroyed " << this);
+Acl::TreePointer
+ACLChecklist::swapAcl(const acl_access * const replacement)
+{
+ const auto old = accessList;
+ changeAcl(replacement);
+ return old;
}
/**
markFinished(accessList->winningAction(), "match");
}
-Acl::Answer const &
-ACLChecklist::fastCheck(const Acl::Tree * list)
+const Acl::Answer &
+ACLChecklist::fastCheck(const ACLList * const list)
{
preCheck("fast ACLs");
asyncCaller_ = false;
// Concurrent checks are not supported, but sequential checks are, and they
// may use a mixture of fastCheck(void) and fastCheck(list) calls.
- const Acl::Tree * const savedList = changeAcl(list);
+ const auto savedList = swapAcl(list);
// assume DENY/ALLOW on mis/matches due to action-free accessList
// matchAndFinish() takes care of the ALLOW case
- if (accessList && cbdataReferenceValid(accessList))
+ if (accessList)
matchAndFinish(); // calls markFinished() on success
if (!finished())
markFinished(ACCESS_DENIED, "ACLs failed to match");
- changeAcl(savedList);
+ changeAcl(&savedList);
occupied_ = false;
return currentAnswer();
}
asyncCaller_ = false;
debugs(28, 5, "aclCheckFast: list: " << accessList);
- const Acl::Tree *acl = cbdataReference(accessList);
- if (acl != nullptr && cbdataReferenceValid(acl)) {
+ if (accessList) {
matchAndFinish(); // calls markFinished() on success
// if finished (on a match or in exceptional cases), stop
if (finished()) {
- cbdataReferenceDone(acl);
occupied_ = false;
return currentAnswer();
}
// There were no rules to match or no rules matched
calcImplicitAnswer();
- cbdataReferenceDone(acl);
occupied_ = false;
return currentAnswer();
void
ACLChecklist::calcImplicitAnswer()
{
- const auto lastAction = (accessList && cbdataReferenceValid(accessList)) ?
+ const auto lastAction = accessList ?
accessList->lastAction() : Acl::Answer(ACCESS_DUNNO);
auto implicitRuleAnswer = Acl::Answer(ACCESS_DUNNO);
if (lastAction == ACCESS_DENIED) // reverse last seen "deny"
*
* If there are no ACLs to check at all, the result becomes ACCESS_ALLOWED.
*/
- Acl::Answer const & fastCheck(const Acl::Tree *list);
+ const Acl::Answer &fastCheck(const ACLList *);
/// If slow lookups are allowed, switches into "async in progress" state.
/// Otherwise, returns false; the caller is expected to handle the failure.
bool goAsync(AsyncStarter, const Acl::Node &);
- /// Matches (or resumes matching of) a child node while maintaning
+ /// Matches (or resumes matching of) a child node at pos while maintaining
/// resumption breadcrumbs if a [grand]child node goes async.
- bool matchChild(const Acl::InnerNode *parent, Acl::Nodes::const_iterator pos, const Acl::Node *child);
+ bool matchChild(const Acl::InnerNode *parent, Acl::Nodes::const_iterator pos);
/// Whether we should continue to match tree nodes or stop/pause.
bool keepMatching() const { return !finished() && !asyncInProgress(); }
virtual void verifyAle() const = 0;
/// change the current ACL list
- /// \return a pointer to the old list value (may be nullptr)
- const Acl::Tree *changeAcl(const Acl::Tree *t) {
- const Acl::Tree *old = accessList;
- if (t != accessList) {
- cbdataReferenceDone(accessList);
- accessList = cbdataReference(t);
- }
- return old;
- }
+ void changeAcl(const acl_access *);
/// remember the name of the last ACL being evaluated
void setLastCheckedName(const SBuf &name) { lastCheckedName_ = name; }
void matchAndFinish();
- const Acl::Tree *accessList;
+ /// \copydoc changeAcl()
+ /// \returns old accessList pointer (that may be nil)
+ Acl::TreePointer swapAcl(const acl_access *);
+
+ Acl::TreePointer accessList;
+
public:
ACLCB *callback;
bool operator ==(const Breadcrumb &b) const { return parent == b.parent && (!parent || position == b.position); }
bool operator !=(const Breadcrumb &b) const { return !this->operator ==(b); }
void clear() { parent = nullptr; }
- const Acl::InnerNode *parent; ///< intermediate node in the ACL tree
+ RefCount<const Acl::InnerNode> parent; ///< intermediate node in the ACL tree
Acl::Nodes::const_iterator position; ///< child position inside parent
};
#include "SquidConfig.h"
#include "src/sbuf/Stream.h"
-#include <set>
#include <algorithm>
-using AclSet = std::set<Acl::Node *>;
-/// Accumulates all ACLs to facilitate their clean deletion despite reuse.
-static AclSet *RegisteredAcls; // TODO: Remove when ACLs are refcounted
-
err_type
FindDenyInfoPage(const Acl::Answer &answer, const bool redirect_allowed)
{
*T = A;
}
+const Acl::Tree &
+Acl::ToTree(const TreePointer * const config)
+{
+ Assure(config);
+ const auto &treePtr = *config;
+ Assure(treePtr);
+ return *treePtr;
+}
+
void
-aclParseAccessLine(const char *directive, ConfigParser &, acl_access **treep)
+aclParseAccessLine(const char *directive, ConfigParser &, acl_access **config)
{
/* first expect either 'allow' or 'deny' */
const char *t = ConfigParser::NextToken();
return;
}
- const int ruleId = ((treep && *treep) ? (*treep)->childrenCount() : 0) + 1;
+ const int ruleId = ((config && *config) ? ToTree(*config).childrenCount() : 0) + 1;
Acl::AndNode *rule = new Acl::AndNode;
rule->context(ToSBuf(directive, '#', ruleId), config_input_line);
/* Append to the end of this list */
+ assert(config);
+ if (!*config)
+ *config = new acl_access();
+ const auto treep = *config;
+
assert(treep);
if (!*treep) {
*treep = new Acl::Tree;
}
(*treep)->add(rule, action);
-
- /* We lock _acl_access structures in ACLChecklist::matchNonBlocking() */
}
// aclParseAclList does not expect or set actions (cf. aclParseAccessLine)
size_t
-aclParseAclList(ConfigParser &, Acl::Tree **treep, const char *label)
+aclParseAclList(ConfigParser &, ACLList **config, const char *label)
{
// accommodate callers unable to convert their ACL list context to string
if (!label)
rule->context(ToSBuf('(', cfg_directive, ' ', label, " line)"), config_input_line);
const auto aclCount = rule->lineParse();
- // We want a cbdata-protected Tree (despite giving it only one child node).
+ // XXX: We have created only one node, and our callers do not support
+ // actions, but we now have to create an action-supporting Acl::Tree because
+ // Checklist needs actions support. TODO: Add actions methods to Acl::Node,
+ // so that Checklist can be satisfied with Acl::AndNode created above.
Acl::Tree *tree = new Acl::Tree;
tree->add(rule);
tree->context(ToSBuf(cfg_directive, ' ', label), config_input_line);
- assert(treep);
- assert(!*treep);
- *treep = tree;
+ assert(config);
+ assert(!*config);
+ *config = new acl_access(tree);
return aclCount;
}
-void
-aclRegister(Acl::Node *acl)
-{
- if (!acl->registered) {
- if (!RegisteredAcls)
- RegisteredAcls = new AclSet;
- RegisteredAcls->insert(acl);
- acl->registered = true;
- }
-}
-
-/// remove registered acl from the centralized deletion set
-static
-void
-aclDeregister(Acl::Node *acl)
-{
- if (acl->registered) {
- if (RegisteredAcls)
- RegisteredAcls->erase(acl);
- acl->registered = false;
- }
-}
-
/*********************/
/* Destroy functions */
/*********************/
-/// called to delete ALL Acls.
-void
-aclDestroyAcls(Acl::Node ** head)
-{
- *head = nullptr; // Config.aclList
- if (AclSet *acls = RegisteredAcls) {
- debugs(28, 8, "deleting all " << acls->size() << " ACLs");
- while (!acls->empty()) {
- auto *acl = *acls->begin();
- // We use centralized deletion (this function) so ~Acl::Node should not
- // delete other ACLs, but we still deregister first to prevent any
- // accesses to the being-deleted Acl::Node via RegisteredAcls.
- assert(acl->registered); // make sure we are making progress
- aclDeregister(acl);
- delete acl;
- }
- }
-}
-
void
aclDestroyAclList(ACLList **list)
{
{
assert(list);
if (*list)
- debugs(28, 3, "destroying: " << *list << ' ' << (*list)->name);
+ debugs(28, 3, "destroying: " << *list << ' ' << ToTree(*list).name);
delete *list;
*list = nullptr;
}
class StoreEntry;
class wordlist;
-/// Register an Acl::Node object for future deletion. Repeated registrations are OK.
-/// \ingroup ACLAPI
-void aclRegister(Acl::Node *acl);
/// \ingroup ACLAPI
void aclDestroyAccessList(acl_access **list);
/// \ingroup ACLAPI
-void aclDestroyAcls(Acl::Node **);
-/// \ingroup ACLAPI
void aclDestroyAclList(ACLList **);
+
/// Parses a single line of a "action followed by acls" directive (e.g., http_access).
-/// \ingroup ACLAPI
-void aclParseAccessLine(const char *directive, ConfigParser &parser, Acl::Tree **);
+void aclParseAccessLine(const char *directive, ConfigParser &, acl_access **);
+
/// Parses a single line of a "some context followed by acls" directive (e.g., note n v).
/// The label parameter identifies the context (for debugging).
/// \returns the number of parsed ACL names
-size_t aclParseAclList(ConfigParser &parser, Acl::Tree **, const char *label);
+size_t aclParseAclList(ConfigParser &, ACLList **, const char *label);
+
/// Template to convert various context labels to strings. \ingroup ACLAPI
template <class Any>
inline size_t
-aclParseAclList(ConfigParser &parser, Acl::Tree **tree, const Any any)
+aclParseAclList(ConfigParser &parser, ACLList ** const tree, const Any any)
{
std::ostringstream buf;
buf << any;
/// \ingroup ACLAPI
void dump_acl_list(StoreEntry * entry, ACLList * head);
+namespace Acl {
+/// convenient and safe access to a stored (and parsed/configured) Tree
+/// \returns **cfg or *cfg->getRaw()
+/// \prec cfg points to a non-nil TreePointer object; ACL parsing code is
+/// written so that ToTree() caller may just check that cfg itself is not nil
+/// (because parsing code never stores nil TreePointer objects).
+const Tree &ToTree(const TreePointer *cfg);
+}
+
#endif /* SQUID_SRC_ACL_GADGETS_H */
{
assert(node != nullptr);
nodes.push_back(node);
- aclRegister(node);
}
// kids use this method to handle [multiple] parse() calls correctly
++t;
debugs(28, 3, "looking for ACL " << t);
- auto *a = Acl::Node::FindByName(t);
+ // XXX: Performance regression: SBuf will allocate memory.
+ const auto a = Acl::Node::FindByName(SBuf(t));
if (a == nullptr) {
debugs(28, DBG_CRITICAL, "ERROR: ACL not found: " << t);
namespace Acl
{
-using Nodes = std::vector<Acl::Node*>; ///< a collection of nodes
+/// operands of a boolean ACL expression, in configuration/evaluation order
+using Nodes = std::vector<Node::Pointer>;
/// An intermediate Acl::Node tree node. Manages a collection of child tree nodes.
class InnerNode: public Acl::Node
{
public:
- // No ~InnerNode() to delete children. They are aclRegister()ed instead.
-
/// Resumes matching (suspended by an async call) at the given position.
bool resumeMatchingAt(ACLChecklist *checklist, Acl::Nodes::const_iterator pos) const;
/* Acl::Node API */
int match(ACLChecklist *checklist) override;
- // XXX: use refcounting instead of raw pointers
- Nodes nodes; ///< children nodes of this intermediate node
+ Nodes nodes; ///< children of this intermediate node
};
} // namespace Acl
/// Can evaluate itself in FilledChecklist context.
/// Does not change during evaluation.
/// \ingroup ACLAPI
-class Node
+class Node: public RefCountable
{
public:
+ using Pointer = RefCount<Node>;
+
void *operator new(size_t);
void operator delete(void *);
- static void ParseAclLine(ConfigParser &parser, Acl::Node **head);
+ /// parses acl directive parts that follow directive name (i.e. "acl")
+ static void ParseNamedAcl(ConfigParser &, NamedAcls *&);
+
static void Initialize();
/// A configured ACL with a given name or nil.
static Acl::Node *FindByName(const SBuf &);
- /// \copydoc FindByName()
- /// \deprecated Use to avoid performance regressions; remove with the last caller.
- static Acl::Node *FindByName(const char *name);
Node();
Node(Node &&) = delete; // no copying of any kind
/// See also: context() and FindByName().
SBuf name;
- char *cfgline;
- Acl::Node *next; // XXX: remove or at least use refcounting
- bool registered; ///< added to the global list of ACLs via aclRegister()
+ char *cfgline = nullptr;
private:
/// Matches the actual data in checklist against this Acl::Node.
/// \see Acl::Node::options()
virtual const Acl::Options &lineOptions() { return Acl::NoOptions(); }
- static void ParseNamed(ConfigParser &, Node **head, const SBuf &name);
+ static void ParseNamed(ConfigParser &, NamedAcls &, const SBuf &name);
};
} // namespace Acl
#include "acl/Tree.h"
#include "wordlist.h"
-CBDATA_NAMESPACED_CLASS_INIT(Acl, Tree);
-
Acl::Answer
Acl::Tree::winningAction() const
{
namespace Acl
{
-/// An ORed set of rules at the top of the ACL expression tree, providing two
-/// unique properties: cbdata protection and optional rule actions.
+/// An ORed set of rules at the top of the ACL expression tree with support for
+/// optional rule actions.
class Tree: public OrNode
{
- // XXX: We should use refcounting instead, but it requires making ACLs
- // refcounted as well. Otherwise, async lookups will reach deleted ACLs.
- CBDATA_CLASS(Tree);
+ MEMPROXY_CLASS(Tree);
public:
/// dumps <name, action, rule, new line> tuples
class ACLChecklist;
class ACLFilledChecklist;
-class ACLList;
class AclDenyInfoList;
class AclSizeLimit;
class Answer;
class ChecklistFiller;
class InnerNode;
+class NamedAcls;
class NotNode;
class OrNode;
class Tree;
/// prepares to parse ACLs configuration
void Init(void);
+/// reconfiguration-safe storage of ACL rules
+using TreePointer = RefCount<Acl::Tree>;
+
} // namespace Acl
typedef void ACLCB(Acl::Answer, void *);
// TODO: Consider renaming all users and removing. Cons: hides the difference
// between ACLList tree without actions and acl_access Tree with actions.
-#define acl_access Acl::Tree
-#define ACLList Acl::Tree
+using acl_access = Acl::TreePointer;
+using ACLList = Acl::TreePointer;
class ExternalACLEntry;
typedef RefCount<ExternalACLEntry> ExternalACLEntryPointer;
}
static void
-dump_acl(StoreEntry * entry, const char *name, Acl::Node * ae)
+dump_acl(StoreEntry *entry, const char *directiveName, Acl::NamedAcls *config)
{
PackableStream os(*entry);
- while (ae != nullptr) {
- debugs(3, 3, "dump_acl: " << name << " " << ae->name);
- ae->dumpWhole(name, os);
- ae = ae->next;
- }
+ Acl::DumpNamedAcls(os, directiveName, config);
}
static void
-parse_acl(Acl::Node ** ae)
+parse_acl(Acl::NamedAcls **config)
{
- Acl::Node::ParseAclLine(LegacyParser, ae);
+ assert(config);
+ Acl::Node::ParseNamedAcl(LegacyParser, *config);
}
static void
-free_acl(Acl::Node ** ae)
+free_acl(Acl::NamedAcls **config)
{
- aclDestroyAcls(ae);
+ Acl::FreeNamedAcls(config);
}
void
{
// XXX: Should dump ACL names like "foo !bar" but dumps parsing context like
// "(clientside_tos 0x11 line)".
- dump_SBufList(entry, head->dump());
+ dump_SBufList(entry, ToTree(head).dump());
}
void
dump_acl_access(StoreEntry * entry, const char *name, acl_access * head)
{
if (head)
- dump_SBufList(entry, head->treeDump(name, &Acl::AllowOrDeny));
+ dump_SBufList(entry, ToTree(head).treeDump(name, &Acl::AllowOrDeny));
}
static void
dump_AuthSchemes(StoreEntry *entry, const char *name, acl_access *authSchemes)
{
if (authSchemes)
- dump_SBufList(entry, authSchemes->treeDump(name, [](const Acl::Answer &action) {
+ dump_SBufList(entry, ToTree(authSchemes).treeDump(name, [](const Acl::Answer &action) {
return Auth::TheConfig.schemeLists.at(action.kind).rawSchemes;
}));
}
#endif /* USE_AUTH */
static void
-ParseAclWithAction(acl_access **access, const Acl::Answer &action, const char *desc, Acl::Node *acl)
+ParseAclWithAction(acl_access **config, const Acl::Answer &action, const char *desc, Acl::Node *acl)
{
- assert(access);
- if (!*access) {
- *access = new Acl::Tree;
- (*access)->context(ToSBuf('(', desc, " rules)"), config_input_line);
+ assert(config);
+ auto &access = *config;
+ if (!access) {
+ const auto tree = new Acl::Tree;
+ tree->context(ToSBuf('(', desc, " rules)"), config_input_line);
+ access = new acl_access(tree);
}
+ assert(access);
+
Acl::AndNode *rule = new Acl::AndNode;
rule->context(ToSBuf('(', desc, " rule)"), config_input_line);
if (acl)
static void dump_sslproxy_ssl_bump(StoreEntry *entry, const char *name, acl_access *ssl_bump)
{
if (ssl_bump)
- dump_SBufList(entry, ssl_bump->treeDump(name, [](const Acl::Answer &action) {
+ dump_SBufList(entry, ToTree(ssl_bump).treeDump(name, [](const Acl::Answer &action) {
return Ssl::BumpModeStr.at(action.kind);
}));
}
static void dump_ftp_epsv(StoreEntry *entry, const char *name, acl_access *ftp_epsv)
{
if (ftp_epsv)
- dump_SBufList(entry, ftp_epsv->treeDump(name, Acl::AllowOrDeny));
+ dump_SBufList(entry, ToTree(ftp_epsv).treeDump(name, Acl::AllowOrDeny));
}
static void free_ftp_epsv(acl_access **ftp_epsv)
"respond"
};
if (access) {
- SBufList lines = access->treeDump(name, [](const Acl::Answer &action) {
+ const auto lines = ToTree(access).treeDump(name, [](const Acl::Answer &action) {
return onErrorTunnelMode.at(action.kind);
});
dump_SBufList(entry, lines);
SBufList line;
line.push_back(name);
line.push_back(proto);
- const auto acld = acls->treeDump("", &Acl::AllowOrDeny);
+ const auto acld = ToTree(acls).treeDump("", &Acl::AllowOrDeny);
line.insert(line.end(), acld.begin(), acld.end());
dump_SBufList(entry, line);
});
NAME: acl
TYPE: acl
-LOC: Config.aclList
+LOC: Config.namedAcls
IF USE_OPENSSL
DEFAULT: ssl::certHasExpired ssl_error X509_V_ERR_CERT_HAS_EXPIRED
DEFAULT: ssl::certNotYetValid ssl_error X509_V_ERR_CERT_NOT_YET_VALID
bool
Client::blockCaching()
{
- if (const Acl::Tree *acl = Config.accessList.storeMiss) {
+ if (const auto acl = Config.accessList.storeMiss) {
// This relatively expensive check is not in StoreEntry::checkCachable:
// That method lacks HttpRequest and may be called too many times.
ACLFilledChecklist ch(acl, originalRequest().getRaw());
Acl::Answer
ACLExternal::aclMatchExternal(external_acl_data *acl, ACLFilledChecklist *ch) const
{
+ // Despite its external_acl C++ type name, acl->def is not an ACL (i.e. not
+ // a reference-counted Acl::Node) and gets invalidated by reconfiguration.
+ // TODO: RefCount external_acl, so that we do not have to bail here.
+ if (!cbdataReferenceValid(acl->def)) {
+ debugs(82, 3, "cannot resume matching; external_acl gone");
+ return ACCESS_DUNNO;
+ }
+
debugs(82, 9, "acl=\"" << acl->def->name << "\"");
ExternalACLEntryPointer entry = ch->extacl_entry;
dumpOptions(os);
if (aclList) {
// TODO: Use Acl::dump() after fixing the XXX in dump_acl_list().
- for (const auto &acl: aclList->treeDump("if", &Acl::AllowOrDeny))
+ for (const auto &acl: ToTree(aclList).treeDump("if", &Acl::AllowOrDeny))
os << ' ' << acl;
}
}
#include "acl/forward.h"
#include "acl/Gadgets.h"
-size_t aclParseAclList(ConfigParser &, Acl::Tree **, const char *) STUB_RETVAL(0)
+size_t aclParseAclList(ConfigParser &, ACLList **, const char *) STUB_RETVAL(0)
#include "auth/UserRequest.h"
#include "compat/cppunit.h"
#include "ConfigParser.h"
+#include "SquidConfig.h"
#include "unitTestMain.h"
#include <stdexcept>
char * line = xstrdup("test max_user_ip -s 1");
/* seed the parser */
ConfigParser::SetCfgLine(line);
- Acl::Node *anACL = nullptr;
ConfigParser LegacyParser;
- Acl::Node::ParseAclLine(LegacyParser, &anACL);
+ Acl::Node::ParseNamedAcl(LegacyParser, Config.namedAcls);
+ CPPUNIT_ASSERT(Config.namedAcls);
+ const auto anACL = Acl::Node::FindByName(SBuf("test"));
+ CPPUNIT_ASSERT(anACL);
ACLMaxUserIP *maxUserIpACL = dynamic_cast<ACLMaxUserIP *>(anACL);
CPPUNIT_ASSERT(maxUserIpACL);
if (maxUserIpACL) {
/* the acl must be vaid */
CPPUNIT_ASSERT_EQUAL(true, maxUserIpACL->valid());
}
- delete anACL;
+ Acl::FreeNamedAcls(&Config.namedAcls);
xfree(line);
}