From 6f58d7d7a975665078c7a748faf88188a34252ae Mon Sep 17 00:00:00 2001 From: Alex Rousskov Date: Mon, 13 May 2013 16:48:23 -0600 Subject: [PATCH] Major ACL handling update, including the following changes: * Expressiveness: Two new Boolean ACLs (all-of and any-of) that allow admins to group ACLs as needed, to express complex conditions more naturally, with fewer squid.conf lines. Conditions such as "(a or b) and (c or d)" are easily expressed now. Explicit groups of ACLs of different types can now be configured, named, and used in any ACL expression. * Correctness and performance: When a slow ACL (that has suspended checks to wait for an async lookup) is ready to resume checking, resume checking from that ACL, instead of rechecking all ACLs for the same action (or the same squid.conf directive) again. * Internals: Store ACL-related configurations as an expression tree, streamlining the code and clearing the way for future math-style/natural ACL conditions support. --- src/AclRegs.cc | 9 +- src/CachePeer.h | 2 +- src/ClientDelayConfig.cc | 2 +- src/ClientDelayConfig.h | 2 +- src/DelayConfig.cc | 2 +- src/DelayPool.h | 3 +- src/HttpHeaderTools.h | 3 +- src/Notes.cc | 6 +- src/Notes.h | 2 +- src/SquidConfig.h | 5 +- src/acl/Acl.cc | 125 ++++------ src/acl/Acl.h | 66 +++-- src/acl/AclNameList.h | 2 +- src/acl/AclSizeLimit.h | 3 +- src/acl/AllOf.cc | 83 +++++++ src/acl/AllOf.h | 33 +++ src/acl/AnyOf.cc | 23 ++ src/acl/AnyOf.h | 27 +++ src/acl/Asn.cc | 11 +- src/acl/Checklist.cc | 450 ++++++++++++++--------------------- src/acl/Checklist.h | 82 ++++--- src/acl/DestinationDomain.cc | 12 +- src/acl/DestinationIp.cc | 15 +- src/acl/FilledChecklist.h | 1 + src/acl/Gadgets.cc | 130 +++++----- src/acl/Gadgets.h | 25 +- src/acl/InnerNode.cc | 110 +++++++++ src/acl/InnerNode.h | 47 ++++ src/acl/Makefile.am | 13 +- src/acl/SourceDomain.cc | 12 +- src/acl/Tree.cc | 76 ++++++ src/acl/Tree.h | 44 ++++ src/acl/forward.h | 30 +++ src/adaptation/AccessRule.cc | 2 +- src/adaptation/AccessRule.h | 4 +- src/adaptation/Config.h | 2 +- src/adaptation/icap/Config.h | 3 +- src/auth/Acl.cc | 6 +- src/auth/AclMaxUserIp.cc | 4 +- src/auth/AclProxyAuth.cc | 11 +- src/cache_cf.cc | 117 ++++----- src/cf.data.pre | 23 ++ src/cf_gen.cc | 2 + src/client_side_request.cc | 3 +- src/client_side_request.h | 3 +- src/defines.h | 1 - src/external_acl.cc | 31 +-- src/globals.h | 2 + src/ident/AclIdent.cc | 20 +- src/ip/QosConfig.h | 2 +- src/log/CustomLog.h | 2 +- src/ssl/ProxyCerts.h | 2 +- 52 files changed, 1039 insertions(+), 657 deletions(-) create mode 100644 src/acl/AllOf.cc create mode 100644 src/acl/AllOf.h create mode 100644 src/acl/AnyOf.cc create mode 100644 src/acl/AnyOf.h create mode 100644 src/acl/InnerNode.cc create mode 100644 src/acl/InnerNode.h create mode 100644 src/acl/Tree.cc create mode 100644 src/acl/Tree.h create mode 100644 src/acl/forward.h diff --git a/src/AclRegs.cc b/src/AclRegs.cc index fb112f18ba..3b5cb0bd86 100644 --- a/src/AclRegs.cc +++ b/src/AclRegs.cc @@ -5,7 +5,8 @@ does not get linked in, because nobody is using these classes by name. */ -#include "acl/Acl.h" +#include "acl/AllOf.h" +#include "acl/AnyOf.h" #if USE_SQUID_EUI #include "acl/Arp.h" #include "acl/Eui64.h" @@ -181,3 +182,9 @@ ACLMaxUserIP ACLMaxUserIP::RegistryEntry_("max_user_ip"); ACL::Prototype ACLTag::RegistryProtoype(&ACLTag::RegistryEntry_, "tag"); ACLStrategised ACLTag::RegistryEntry_(new ACLStringData, ACLTagStrategy::Instance(), "tag"); + +ACL::Prototype Acl::AnyOf::RegistryProtoype(&Acl::AnyOf::RegistryEntry_, "any-of"); +Acl::AnyOf Acl::AnyOf::RegistryEntry_; + +ACL::Prototype Acl::AllOf::RegistryProtoype(&Acl::AllOf::RegistryEntry_, "all-of"); +Acl::AllOf Acl::AllOf::RegistryEntry_; diff --git a/src/CachePeer.h b/src/CachePeer.h index ec00d3031e..67aebfb26d 100644 --- a/src/CachePeer.h +++ b/src/CachePeer.h @@ -29,6 +29,7 @@ * */ +#include "acl/forward.h" #include "enums.h" #include "icp_opcode.h" #include "ip/Address.h" @@ -40,7 +41,6 @@ #include #endif -class acl_access; class CachePeerDomainList; class NeighborTypeDomainList; class PeerDigest; diff --git a/src/ClientDelayConfig.cc b/src/ClientDelayConfig.cc index d72e7a282d..0d95212f78 100644 --- a/src/ClientDelayConfig.cc +++ b/src/ClientDelayConfig.cc @@ -83,7 +83,7 @@ void ClientDelayConfig::parsePoolAccess(ConfigParser &parser) } --pool; - aclParseAccessLine(parser, &pools[pool].access); + aclParseAccessLine("client_delay_access", parser, &pools[pool].access); } void ClientDelayConfig::clean() diff --git a/src/ClientDelayConfig.h b/src/ClientDelayConfig.h index d48f93d600..f3fa2d4eb0 100644 --- a/src/ClientDelayConfig.h +++ b/src/ClientDelayConfig.h @@ -2,8 +2,8 @@ #define SQUID_CLIENTDELAYCONFIG_H #include "Array.h" +#include "acl/forward.h" class StoreEntry; -class acl_access; class ConfigParser; /// \ingroup DelayPoolsAPI diff --git a/src/DelayConfig.cc b/src/DelayConfig.cc index 70dbebbb4f..070c55d7af 100644 --- a/src/DelayConfig.cc +++ b/src/DelayConfig.cc @@ -113,7 +113,7 @@ DelayConfig::parsePoolAccess(ConfigParser &parser) } --pool; - aclParseAccessLine(parser, &DelayPools::delay_data[pool].access); + aclParseAccessLine("delay_access", parser, &DelayPools::delay_data[pool].access); } void diff --git a/src/DelayPool.h b/src/DelayPool.h index df5224c0f5..36e1fc04bb 100644 --- a/src/DelayPool.h +++ b/src/DelayPool.h @@ -39,13 +39,12 @@ #if USE_DELAY_POOLS #include "CompositePoolNode.h" +#include "acl/forward.h" class StoreEntry; class CommonPool; -class acl_access; - /// \ingroup DelayPoolsAPI class DelayPool { diff --git a/src/HttpHeaderTools.h b/src/HttpHeaderTools.h index dafa78734a..5f097977d8 100644 --- a/src/HttpHeaderTools.h +++ b/src/HttpHeaderTools.h @@ -1,6 +1,7 @@ #ifndef SQUID_HTTPHEADERTOOLS_H #define SQUID_HTTPHEADERTOOLS_H +#include "acl/forward.h" #include "format/Format.h" #include "HttpHeader.h" #include "typedefs.h" @@ -15,8 +16,6 @@ #include #endif -class acl_access; -class ACLList; class HeaderWithAcl; class HttpHeader; class HttpHeaderFieldInfo; diff --git a/src/Notes.cc b/src/Notes.cc index 131cf255ac..7cddf8b520 100644 --- a/src/Notes.cc +++ b/src/Notes.cc @@ -96,7 +96,11 @@ Notes::parse(ConfigParser &parser) ConfigParser::ParseQuotedString(&value); Note::Pointer note = add(key); Note::Value::Pointer noteValue = note->addValue(value); - aclParseAclList(parser, ¬eValue->aclList); + + String label(key); + label.append('='); + label.append(value); + aclParseAclList(parser, ¬eValue->aclList, label.termedBuf()); if (blacklisted) { for (int i = 0; blacklisted[i] != NULL; ++i) { diff --git a/src/Notes.h b/src/Notes.h index 6d9fd3ca9e..a595ad03f0 100644 --- a/src/Notes.h +++ b/src/Notes.h @@ -1,6 +1,7 @@ #ifndef SQUID_NOTES_H #define SQUID_NOTES_H +#include "acl/forward.h" #include "Array.h" #include "base/RefCount.h" #include "CbDataList.h" @@ -14,7 +15,6 @@ class HttpRequest; class HttpReply; -class ACLList; /** * Used to store a note configuration. The notes are custom key:value diff --git a/src/SquidConfig.h b/src/SquidConfig.h index b64ae89356..db3b02f25c 100644 --- a/src/SquidConfig.h +++ b/src/SquidConfig.h @@ -29,7 +29,7 @@ * */ -#include "acl/AclAddress.h" +#include "acl/forward.h" #include "base/RefCount.h" #include "ClientDelayConfig.h" #include "DelayConfig.h" @@ -46,9 +46,6 @@ class sslproxy_cert_sign; class sslproxy_cert_adapt; #endif -class acl_access; -class AclSizeLimit; -class AclDenyInfoList; namespace Mgr { class ActionPasswordList; diff --git a/src/acl/Acl.cc b/src/acl/Acl.cc index 2eeef57fab..b1e3fd8b91 100644 --- a/src/acl/Acl.cc +++ b/src/acl/Acl.cc @@ -1,6 +1,5 @@ /* * DEBUG: section 28 Access Control - * AUTHOR: Duane Wessels * * SQUID Web Proxy Cache http://www.squid-cache.org/ * ---------------------------------------------------------- @@ -38,6 +37,7 @@ #include "Debug.h" #include "dlink.h" #include "globals.h" +#include "profiler/Profiler.h" #include "SquidConfig.h" const ACLFlag ACLFlags::NoFlags[1] = {ACL_F_END}; @@ -137,7 +137,8 @@ ACL::Factory (char const *type) ACL::ACL() : cfgline(NULL), - next(NULL) + next(NULL), + registered(false) { *name = 0; } @@ -147,6 +148,46 @@ bool ACL::valid () const return true; } +bool +ACL::matches(ACLChecklist *checklist) const +{ + PROF_start(ACL_matches); + debugs(28, 5, "checking " << name); + + // XXX: AclMatchedName does not contain a matched ACL name when the acl + // does not match. It contains the last (usually leaf) ACL name checked + // (or is NULL if no ACLs were checked). + AclMatchedName = name; + + int result = 0; + if (!checklist->hasRequest() && requiresRequest()) { + debugs(28, DBG_IMPORTANT, "WARNING: " << name << " ACL is used in " << + "context without an HTTP request. Assuming mismatch."); + } else if (!checklist->hasReply() && requiresReply()) { + debugs(28, DBG_IMPORTANT, "WARNING: " << name << " ACL is used in " << + "context without an HTTP response. Assuming mismatch."); + } else { + // have to cast because old match() API is missing const + result = const_cast(this)->match(checklist); + } + + const char *extra = checklist->asyncInProgress() ? " async" : ""; + debugs(28, 3, "checked: " << name << " = " << result << extra); + PROF_stop(ACL_matches); + return result == 1; // true for match; false for everything else +} + +void +ACL::context(const char *aName, const char *aCfgLine) +{ + name[0] = '\0'; + if (aName) + xstrncpy(name, aName, ACL_NAME_SZ-1); + safe_free(cfgline); + if (aCfgLine) + cfgline = xstrdup(aCfgLine); +} + void ACL::ParseAclLine(ConfigParser &parser, ACL ** head) { @@ -215,8 +256,7 @@ ACL::ParseAclLine(ConfigParser &parser, ACL ** head) if ((A = FindByName(aclname)) == NULL) { debugs(28, 3, "aclParseAclLine: Creating ACL '" << aclname << "'"); A = ACL::Factory(theType); - xstrncpy(A->name, aclname, ACL_NAME_SZ); - A->cfgline = xstrdup(config_input_line); + A->context(aclname, config_input_line); new_acl = 1; } else { if (strcmp (A->typeString(),theType) ) { @@ -259,6 +299,9 @@ ACL::ParseAclLine(ConfigParser &parser, ACL ** head) } /* append */ + assert(head && *head == Config.aclList); + A->registered = true; + while (*head) head = &(*head)->next; @@ -271,18 +314,6 @@ ACL::isProxyAuth() const return false; } -ACLList::ACLList() : op (1), _acl (NULL), next (NULL) -{} - -void -ACLList::negated(bool isNegated) -{ - if (isNegated) - op = 0; - else - op = 1; -} - /* ACL result caching routines */ int @@ -359,49 +390,6 @@ ACL::requiresRequest() const return false; } -int -ACL::checklistMatches(ACLChecklist *checklist) -{ - int rv; - - if (!checklist->hasRequest() && requiresRequest()) { - debugs(28, DBG_IMPORTANT, "ACL::checklistMatches WARNING: '" << name << "' ACL is used but there is no HTTP request -- not matching."); - return 0; - } - - if (!checklist->hasReply() && requiresReply()) { - debugs(28, DBG_IMPORTANT, "ACL::checklistMatches WARNING: '" << name << "' ACL is used but there is no HTTP reply -- not matching."); - return 0; - } - - debugs(28, 3, "ACL::checklistMatches: checking '" << name << "'"); - rv= match(checklist); - debugs(28, 3, "ACL::ChecklistMatches: result for '" << name << "' is " << rv); - return rv; -} - -bool -ACLList::matches (ACLChecklist *checklist) const -{ - assert (_acl); - // XXX: AclMatchedName does not contain a matched ACL name when the acl - // does not match (or contains stale name if no ACLs are checked). In - // either case, we get misleading debugging and possibly incorrect error - // messages. Unfortunately, deny_info's "when none http_access - // lines match" exception essentially requires this mess. - // TODO: Rework by using an acl-free deny_info for the no-match cases? - AclMatchedName = _acl->name; - debugs(28, 3, "ACLList::matches: checking " << (op ? null_string : "!") << _acl->name); - - if (_acl->checklistMatches(checklist) != op) { - debugs(28, 4, "ACLList::matches: result is false"); - return false; - } - - debugs(28, 4, "ACLList::matches: result is true"); - return true; -} - /*********************/ /* Destroy functions */ /*********************/ @@ -410,26 +398,9 @@ ACL::~ACL() { debugs(28, 3, "ACL::~ACL: '" << cfgline << "'"); safe_free(cfgline); + AclMatchedName = NULL; // in case it was pointing to our name } -/* to be split into separate files in the future */ - -CBDATA_CLASS_INIT(acl_access); - -void * -acl_access::operator new (size_t) -{ - CBDATA_INIT_TYPE(acl_access); - acl_access *result = cbdataAlloc(acl_access); - return result; -} - -void -acl_access::operator delete (void *address) -{ - acl_access *t = static_cast(address); - cbdataFree(t); -} ACL::Prototype::Prototype() : prototype (NULL), typeString (NULL) {} diff --git a/src/acl/Acl.h b/src/acl/Acl.h index e7ae5af6f3..0b233267de 100644 --- a/src/acl/Acl.h +++ b/src/acl/Acl.h @@ -33,6 +33,7 @@ #ifndef SQUID_ACL_H #define SQUID_ACL_H +#include "acl/forward.h" #include "Array.h" #include "cbdata.h" #include "defines.h" @@ -47,8 +48,6 @@ #endif class ConfigParser; -class ACLChecklist; -class ACLList; typedef char ACLFlag; // ACLData Flags @@ -90,6 +89,10 @@ public: static const ACLFlag NoFlags[1]; ///< An empty flags list }; + +/// A configurable condition. A node in the ACL expression tree. +/// Can evaluate itself in FilledChecklist context. +/// Does not change during evaluation. /// \ingroup ACLAPI class ACL { @@ -106,17 +109,25 @@ public: ACL(); explicit ACL(const ACLFlag flgs[]) : cfgline(NULL), next(NULL), flags(flgs) { memset(name, '\0', sizeof(name)); } virtual ~ACL(); + + /// sets user-specified ACL name and squid.conf context + void context(const char *name, const char *configuration); + + /// Orchestrates matching checklist against the ACL using match(), + /// after checking preconditions and while providing debugging. + /// Returns true if and only if there was a successful match. + /// Updates the checklist state on match, async, and failure. + bool matches(ACLChecklist *checklist) const; + virtual ACL *clone()const = 0; + + /// parses node represenation in squid.conf; dies on failures virtual void parse() = 0; virtual char const *typeString() const = 0; virtual bool isProxyAuth() const; - virtual bool requiresRequest() const; - virtual bool requiresReply() const; - virtual int match(ACLChecklist * checklist) = 0; virtual wordlist *dump() const = 0; virtual bool empty () const = 0; virtual bool valid () const; - int checklistMatches(ACLChecklist *); int cacheMatchAcl(dlink_list * cache, ACLChecklist *); virtual int matchForCache(ACLChecklist *checklist); @@ -127,6 +138,7 @@ public: char *cfgline; ACL *next; ACLFlags flags; ///< The list of given ACL flags + bool registered; ///< added to Config.aclList and can be reused via by FindByName() public: @@ -151,6 +163,15 @@ public: typedef Vector::const_iterator const_iterator; void registerMe(); }; + +private: + /// Matches the actual data in checklist against this ACL. + virtual int match(ACLChecklist *checklist) = 0; // XXX: missing const + + /// whether our (i.e. shallow) match() requires checklist to have a request + virtual bool requiresRequest() const; + /// whether our (i.e. shallow) match() requires checklist to have a reply + virtual bool requiresReply() const; }; /// \ingroup ACLAPI @@ -210,39 +231,6 @@ operator <<(std::ostream &o, const allow_t a) return o; } -/// \ingroup ACLAPI -class acl_access -{ - -public: - void *operator new(size_t); - void operator delete(void *); - allow_t allow; - ACLList *aclList; - char *cfgline; - acl_access *next; - -private: - CBDATA_CLASS(acl_access); -}; - -/// \ingroup ACLAPI -class ACLList -{ - -public: - MEMPROXY_CLASS(ACLList); - - ACLList(); - void negated(bool isNegated); - bool matches (ACLChecklist *)const; - int op; - ACL *_acl; - ACLList *next; -}; - -MEMPROXY_CLASS_INLINE(ACLList); - /// \ingroup ACLAPI class acl_proxy_auth_match_cache { diff --git a/src/acl/AclNameList.h b/src/acl/AclNameList.h index 5acbc7ba01..0b2825c0f5 100644 --- a/src/acl/AclNameList.h +++ b/src/acl/AclNameList.h @@ -29,7 +29,7 @@ * */ -#include "defines.h" +#include "acl/forward.h" /// list of name-based ACLs. Currently a POD. class AclNameList diff --git a/src/acl/AclSizeLimit.h b/src/acl/AclSizeLimit.h index ed6e4f0401..be8a4bb406 100644 --- a/src/acl/AclSizeLimit.h +++ b/src/acl/AclSizeLimit.h @@ -29,7 +29,8 @@ * */ -class ACLList; +#include "acl/forward.h" + /// representation of a class of Size-limit ACLs // a POD. TODO: convert to new ACL framework class AclSizeLimit diff --git a/src/acl/AllOf.cc b/src/acl/AllOf.cc new file mode 100644 index 0000000000..57e67a3527 --- /dev/null +++ b/src/acl/AllOf.cc @@ -0,0 +1,83 @@ +#include "squid.h" +#include "acl/AllOf.h" +#include "acl/Checklist.h" +#include "acl/BoolOps.h" +#include "globals.h" +#include "MemBuf.h" + + +char const * +Acl::AllOf::typeString() const +{ + return "all-of"; +} + +ACL * +Acl::AllOf::clone() const +{ + return new AllOf; +} + +wordlist* +Acl::AllOf::dump() const +{ + return empty() ? NULL : nodes.front()->dump(); +} + +int +Acl::AllOf::doMatch(ACLChecklist *checklist, Nodes::const_iterator start) const +{ + assert(start == nodes.begin()); // we only have one node + + // avoid dereferencing invalid start + if (empty()) + return 1; // not 0 because in math empty product equals identity + + if (checklist->matchChild(this, start, *start)) + return 1; // match + + return checklist->keepMatching() ? 0 : -1; +} + +// called once per "acl name all-of name1 name2 ...." line +void +Acl::AllOf::parse() +{ + Acl::InnerNode *whole = NULL; + ACL *oldNode = empty() ? NULL : nodes.front(); + + // optimization: this logic reduces subtree hight (number of tree levels) + if (Acl::OrNode *oldWhole = dynamic_cast(oldNode)) { + // this acl saw multiple lines before; add another one to the old node + whole = oldWhole; + } else if (oldNode) { + // this acl saw a single line before; create a new OR inner node + + MemBuf wholeCtx; + wholeCtx.init(); + wholeCtx.Printf("(%s lines)", name); + wholeCtx.terminate(); + + Acl::OrNode *newWhole = new Acl::OrNode; + newWhole->context(wholeCtx.content(), oldNode->cfgline); + newWhole->add(oldNode); // old (i.e. first) line + nodes.front() = whole = newWhole; + } else { + // this is the first line for this acl; just use it as is + whole = this; + } + + assert(whole); + const int lineId = whole->childrenCount() + 1; + + MemBuf lineCtx; + lineCtx.init(); + lineCtx.Printf("(%s line #%d)", name, lineId); + lineCtx.terminate(); + + Acl::AndNode *line = new AndNode; + line->context(lineCtx.content(), config_input_line); + line->lineParse(); + + whole->add(line); +} diff --git a/src/acl/AllOf.h b/src/acl/AllOf.h new file mode 100644 index 0000000000..7a7f4cebb5 --- /dev/null +++ b/src/acl/AllOf.h @@ -0,0 +1,33 @@ +#ifndef SQUID_ACL_ALL_OF_H +#define SQUID_ACL_ALL_OF_H + +#include "acl/InnerNode.h" + +namespace Acl { + +/// Configurable all-of ACL. Each ACL line is a conjuction of ACLs. +/// Uses AndNode and OrNode to handle squid.conf configuration where multiple +/// acl all-of lines are always ORed together. +class AllOf: public Acl::InnerNode +{ +public: + MEMPROXY_CLASS(AllOf); + + /* ACL API */ + virtual char const *typeString() const; + virtual ACL *clone() const; + virtual void parse(); + virtual wordlist *dump() const; + +private: + /* Acl::InnerNode API */ + virtual int doMatch(ACLChecklist *checklist, Nodes::const_iterator start) const; + + static Prototype RegistryProtoype; + static AllOf RegistryEntry_; +}; +MEMPROXY_CLASS_INLINE(Acl::AllOf); + +} // namespace Acl + +#endif /* SQUID_ACL_ALL_OF_H */ diff --git a/src/acl/AnyOf.cc b/src/acl/AnyOf.cc new file mode 100644 index 0000000000..845567518e --- /dev/null +++ b/src/acl/AnyOf.cc @@ -0,0 +1,23 @@ +#include "squid.h" +#include "acl/AnyOf.h" + +char const * +Acl::AnyOf::typeString() const +{ + return "any-of"; +} + +ACL * +Acl::AnyOf::clone() const +{ + return new AnyOf; +} + +// called once per "acl name any-of name1 name2 ...." line +// but since multiple lines are ORed, the line boundary does not matter, +// so we flatten the tree into one line/level here to minimize overheads +void +Acl::AnyOf::parse() +{ + lineParse(); +} diff --git a/src/acl/AnyOf.h b/src/acl/AnyOf.h new file mode 100644 index 0000000000..e1536eeec8 --- /dev/null +++ b/src/acl/AnyOf.h @@ -0,0 +1,27 @@ +#ifndef SQUID_ACL_ANY_OF_H +#define SQUID_ACL_ANY_OF_H + +#include "acl/BoolOps.h" + +namespace Acl { + +/// Configurable any-of ACL. Each ACL line is a disjuction of ACLs. +class AnyOf: public Acl::OrNode +{ +public: + MEMPROXY_CLASS(AnyOf); + + /* ACL API */ + virtual char const *typeString() const; + virtual ACL *clone() const; + virtual void parse(); + +private: + static Prototype RegistryProtoype; + static AnyOf RegistryEntry_; +}; +MEMPROXY_CLASS_INLINE(Acl::AnyOf); + +} // namespace Acl + +#endif /* SQUID_ACL_ANY_OF_H */ diff --git a/src/acl/Asn.cc b/src/acl/Asn.cc index 43fe185973..f7d3ca447b 100644 --- a/src/acl/Asn.cc +++ b/src/acl/Asn.cc @@ -39,7 +39,7 @@ #include "acl/DestinationIp.h" #include "acl/SourceAsn.h" #include "cache_cf.h" -#include "forward.h" +#include "src/forward.h" #include "HttpReply.h" #include "HttpRequest.h" #include "ipcache.h" @@ -638,14 +638,13 @@ ACLDestinationASNStrategy::match (ACLData * &data, ACLFilledChecklist /* No entry in cache, lookup not attempted */ /* XXX FIXME: allow accessing the acl name here */ debugs(28, 3, "asnMatchAcl: Can't yet compare '" << "unknown" /*name*/ << "' ACL for '" << checklist->request->GetHost() << "'"); - checklist->changeState (DestinationIPLookup::Instance()); - } else { + if (checklist->goAsync(DestinationIPLookup::Instance())) + return -1; + // else fall through to noaddr match, hiding the lookup failure (XXX) + } Ip::Address noaddr; noaddr.SetNoAddr(); return data->match(noaddr); - } - - return 0; } ACLDestinationASNStrategy * diff --git a/src/acl/Checklist.cc b/src/acl/Checklist.cc index 9b98028c16..a4cec177f8 100644 --- a/src/acl/Checklist.cc +++ b/src/acl/Checklist.cc @@ -1,66 +1,24 @@ /* * DEBUG: section 28 Access Control - * AUTHOR: Duane Wessels - * - * SQUID Web Proxy Cache http://www.squid-cache.org/ - * ---------------------------------------------------------- - * - * Squid is the result of efforts by numerous individuals from - * the Internet community; see the CONTRIBUTORS file for full - * details. Many organizations have provided support for Squid's - * development; see the SPONSORS file for full details. Squid is - * Copyrighted (C) 2001 by the Regents of the University of - * California; see the COPYRIGHT file for full details. Squid - * incorporates software developed and/or copyrighted by other - * sources; see the CREDITS file for full details. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. - * - * Copyright (c) 2003, Robert Collins */ #include "squid.h" #include "acl/Checklist.h" +#include "acl/Tree.h" #include "Debug.h" #include "profiler/Profiler.h" -void -ACLChecklist::matchNonBlocking() +/// common parts of nonBlockingCheck() and resumeNonBlockingCheck() +bool +ACLChecklist::prepNonBlocking() { - if (checking()) - return; + assert(accessList); if (callerGone()) { checkCallback(ACCESS_DUNNO); // the answer does not really matter - return; - } - - /** The ACL List should NEVER be NULL when calling this method. - * Always caller should check for NULL and handle appropriate to its needs first. - * We cannot select a sensible default for all callers here. */ - if (accessList == NULL) { - debugs(28, DBG_CRITICAL, "SECURITY ERROR: ACL " << this << " checked with nothing to match against!!"); - checkCallback(ACCESS_DUNNO); - return; + return false; } - allow_t lastSeenKeyword = ACCESS_DUNNO; - /* NOTE: This holds a cbdata reference to the current access_list - * entry, not the whole list. - */ - while (accessList != NULL) { /** \par * If the _acl_access is no longer valid (i.e. its been * freed because of a reconfigure), then bail with ACCESS_DUNNO. @@ -70,71 +28,34 @@ ACLChecklist::matchNonBlocking() cbdataReferenceDone(accessList); debugs(28, 4, "ACLChecklist::check: " << this << " accessList is invalid"); checkCallback(ACCESS_DUNNO); - return; + return false; } - checking (true); - checkAccessList(); - checking (false); - - if (asyncInProgress()) { - return; - } + // If doNonBlocking() was called for a finished() checklist to call + // the callbacks, then do not try to match again. XXX: resumeNonBlockingCheck() should check for this instead. + if (!finished()) + return true; - if (finished()) { /** \par * Either the request is allowed, denied, requires authentication. */ - debugs(28, 3, "ACLChecklist::check: " << this << " match found, calling back with " << currentAnswer()); + debugs(28, 3, this << " calling back with " << currentAnswer()); cbdataReferenceDone(accessList); /* A */ checkCallback(currentAnswer()); /* From here on in, this may be invalid */ - return; - } - - lastSeenKeyword = accessList->allow; - - /* - * Reference the next access entry - */ - const acl_access *A = accessList; - - assert (A); - - accessList = cbdataReference(A->next); - - cbdataReferenceDone(A); - } - - calcImplicitAnswer(lastSeenKeyword); - checkCallback(currentAnswer()); -} - -bool -ACLChecklist::asyncNeeded() const -{ - return state_ != NullState::Instance(); -} - -bool -ACLChecklist::asyncInProgress() const -{ - return async_; + return false; } void -ACLChecklist::asyncInProgress(bool const newAsync) +ACLChecklist::completeNonBlocking() { - assert (!finished() && !(asyncInProgress() && newAsync)); - async_ = newAsync; - debugs(28, 3, "ACLChecklist::asyncInProgress: " << this << - " async set to " << async_); -} + assert(!asyncInProgress()); -bool -ACLChecklist::finished() const -{ - return finished_; + if (!finished()) + calcImplicitAnswer(); + + cbdataReferenceDone(accessList); + checkCallback(currentAnswer()); } void @@ -151,25 +72,77 @@ void ACLChecklist::preCheck(const char *what) { debugs(28, 3, HERE << this << " checking " << what); + AclMatchedName = NULL; finished_ = false; } -void -ACLChecklist::checkAccessList() +bool +ACLChecklist::matchChild(const Acl::InnerNode *current, Acl::Nodes::const_iterator pos, const ACL *child) { - debugs(28, 3, HERE << this << " checking '" << accessList->cfgline << "'"); - /* does the current AND clause match */ - if (matchAclList(accessList->aclList, false)) - markFinished(accessList->allow, "first matching rule won"); + assert(current && child); + + // Remember the curernt tree location to prevent "async loop" cases where + // the same child node wants to go async more than once. + matchLoc_ = Breadcrumb(current, pos); + + // if there are any breadcrumbs left, then follow them on the way down + bool result = false; + if (matchPath.empty()) { + result = child->matches(this); + } else { + const Breadcrumb top(matchPath.top()); + assert(child == top.parent); + matchPath.pop(); + result = top.parent->resumeMatchingAt(this, top.position); + } + + if (asyncInProgress()) { + // We get here for node N that called goAsync() and then, as the call + // stack unwinds, for the nodes higher in the ACL tree that led to N. + matchPath.push(Breadcrumb(current, pos)); + } else { + asyncLoc_.clear(); + } - // If we are not finished() here, the caller must distinguish between - // slow async calls and pure rule mismatches using asyncInProgress(). + matchLoc_.clear(); + return result; } -void -ACLChecklist::checkForAsync() +bool +ACLChecklist::goAsync(AsyncState *state) { - asyncState()->checkForAsync(this); + assert(state); + assert(!asyncInProgress()); + assert(matchLoc_.parent); + + // TODO: add a once-in-a-while WARNING about fast directive using slow ACL? + if (!asyncCaller_) { + debugs(28, 2, this << " a fast-only directive uses a slow ACL!"); + return false; + } + + // TODO: add a once-in-a-while WARNING about async loops? + if (matchLoc_ == asyncLoc_) { + debugs(28, 2, this << " a slow ACL resumes by going async again!"); + return false; + } + + asyncLoc_ = matchLoc_; // prevent async loops + + asyncStage_ = asyncStarting; + changeState(state); + state->checkForAsync(this); // this is supposed to go async + + // Did AsyncState object actually go async? If not, tell the caller. + if (asyncStage_ != asyncStarting) { + assert(asyncStage_ == asyncFailed); + asyncStage_ = asyncNone; // sanity restored + return false; + } + + // yes, we must pause until the async callback calls resumeNonBlockingCheck + asyncStage_ = asyncRunning; + return true; } // ACLFilledChecklist overwrites this to unclock something before we @@ -190,138 +163,15 @@ ACLChecklist::checkCallback(allow_t answer) delete this; } -/// An ACLChecklist::matchNodes() wrapper to simplify profiling. -bool -ACLChecklist::matchAclList(const ACLList * head, bool const fast) -{ - // TODO: remove by using object con/destruction-based PROF_* macros. - PROF_start(aclMatchAclList); - const bool result = matchNodes(head, fast); - PROF_stop(aclMatchAclList); - return result; -} - -/** Returns true if and only if there was a match. If false is returned: - finished() indicates an error or exception of some kind, while - !finished() means there was a mismatch or an allowed slow async call. - If async calls are allowed (i.e. 'fast' was false), then those last - two cases can be distinguished using asyncInProgress(). -*/ -bool -ACLChecklist::matchNodes(const ACLList * head, bool const fast) -{ - assert(!finished()); - - for (const ACLList *node = head; node; node = node->next) { - - const NodeMatchingResult resultBeforeAsync = matchNode(*node, fast); - - if (resultBeforeAsync == nmrMatch) - continue; - - if (resultBeforeAsync == nmrMismatch || resultBeforeAsync == nmrFinished) - return false; - - assert(resultBeforeAsync == nmrNeedsAsync); - - // Ideally, this should be inside match() itself, but that requires - // prohibiting slow ACLs in options that do not support them. - // TODO: rename to maybeStartAsync()? - checkForAsync(); - - // Some match() code claims that an async lookup is needed, but then - // fails to start an async lookup when given a chance. We catch such - // cases here and call matchNode() again, hoping that some cached data - // prevents us from going async again. - // This is inefficient and ugly, but fixing all match() code, including - // the code it calls, such as ipcache_nbgethostbyname(), takes time. - if (!asyncInProgress()) { // failed to start an async operation - - if (finished()) { - debugs(28, 3, HERE << this << " finished after failing to go async: " << currentAnswer()); - return false; // an exceptional case - } - - const NodeMatchingResult resultAfterAsync = matchNode(*node, true); - // the second call disables slow checks so we cannot go async again - assert(resultAfterAsync != nmrNeedsAsync); - if (resultAfterAsync == nmrMatch) - continue; - - assert(resultAfterAsync == nmrMismatch || resultAfterAsync == nmrFinished); - return false; - } - - assert(!finished()); // async operation is truly asynchronous - debugs(28, 3, HERE << this << " awaiting async operation"); - return false; - } - - debugs(28, 3, HERE << this << " success: all ACLs matched"); - return true; -} - -/// Check whether a single ACL matches, returning NodeMatchingResult -ACLChecklist::NodeMatchingResult -ACLChecklist::matchNode(const ACLList &node, bool const fast) -{ - const bool nodeMatched = node.matches(this); - const bool needsAsync = asyncNeeded(); - const bool matchFinished = finished(); - - debugs(28, 3, HERE << this << - " matched=" << nodeMatched << - " async=" << needsAsync << - " finished=" << matchFinished); - - /* There are eight possible outcomes of the matches() call based on - (matched, async, finished) permutations. We support these four: - matched,!async,!finished: a match (must check next rule node) - !matched,!async,!finished: a mismatch (whole rule fails to match) - !matched,!async,finished: error or special condition (propagate) - !matched,async,!finished: ACL needs to make an async call (pause) - */ - - if (nodeMatched) { - // matches() should return false in all special cases - assert(!needsAsync && !matchFinished); - return nmrMatch; - } - - if (matchFinished) { - // we cannot be done and need an async call at the same time - assert(!needsAsync); - debugs(28, 3, HERE << this << " exception: " << currentAnswer()); - return nmrFinished; - } - - if (!needsAsync) { - debugs(28, 3, HERE << this << " simple mismatch"); - return nmrMismatch; - } - - /* we need an async call */ - - if (fast) { - changeState(NullState::Instance()); // disable async checks - markFinished(ACCESS_DUNNO, "async required but prohibited"); - debugs(28, 3, HERE << this << " DUNNO because cannot async"); - return nmrFinished; - } - - debugs(28, 3, HERE << this << " going async"); - return nmrNeedsAsync; -} - ACLChecklist::ACLChecklist() : accessList (NULL), callback (NULL), callback_data (NULL), - async_(false), + asyncCaller_(false), finished_(false), allow_(ACCESS_DENIED), - state_(NullState::Instance()), - checking_(false) + asyncStage_(asyncNone), + state_(NullState::Instance()) { } @@ -334,12 +184,6 @@ ACLChecklist::~ACLChecklist() debugs(28, 4, "ACLChecklist::~ACLChecklist: destroyed " << this); } -void -ACLChecklist::AsyncState::changeState (ACLChecklist *checklist, AsyncState *newState) const -{ - checklist->changeState(newState); -} - ACLChecklist::NullState * ACLChecklist::NullState::Instance() { @@ -348,7 +192,9 @@ ACLChecklist::NullState::Instance() void ACLChecklist::NullState::checkForAsync(ACLChecklist *) const -{} +{ + assert(false); // or the Checklist will never get out of the async state +} ACLChecklist::NullState ACLChecklist::NullState::_instance; @@ -381,21 +227,91 @@ ACLChecklist::nonBlockingCheck(ACLCB * callback_, void *callback_data_) preCheck("slow rules"); callback = callback_; callback_data = cbdataReference(callback_data_); - matchNonBlocking(); + asyncCaller_ = true; + + /** The ACL List should NEVER be NULL when calling this method. + * Always caller should check for NULL and handle appropriate to its needs first. + * We cannot select a sensible default for all callers here. */ + if (accessList == NULL) { + debugs(28, DBG_CRITICAL, "SECURITY ERROR: ACL " << this << " checked with nothing to match against!!"); + checkCallback(ACCESS_DUNNO); + return; + } + + if (prepNonBlocking()) { + matchAndFinish(); // calls markFinished() on success + if (!asyncInProgress()) + completeNonBlocking(); + } // else checkCallback() has been called +} + +void +ACLChecklist::resumeNonBlockingCheck(AsyncState *state) +{ + assert(asyncState() == state); + changeState(NullState::Instance()); + + if (asyncStage_ == asyncStarting) { // oops, we did not really go async + asyncStage_ = asyncFailed; // goAsync() checks for that + // Do not fall through to resume checks from the async callback. Let + // the still-pending(!) goAsync() notice and notify its caller instead. + return; + } + assert(asyncStage_ == asyncRunning); + asyncStage_ = asyncNone; + + assert(!matchPath.empty()); + + if (!prepNonBlocking()) + return; // checkCallback() has been called + + matchAndFinish(); + + if (asyncInProgress()) + assert(!matchPath.empty()); // we have breadcrumbs to resume matching + else + completeNonBlocking(); +} + +/// performs (or resumes) an ACL tree match and, if successful, sets the action +void +ACLChecklist::matchAndFinish() { + bool result = false; + if (matchPath.empty()) { + result = accessList->matches(this); + } else { + const Breadcrumb top(matchPath.top()); + matchPath.pop(); + result = top.parent->resumeMatchingAt(this, top.position); + } + + if (result) // the entire tree matched + markFinished(accessList->winningAction(), "match"); } allow_t const & -ACLChecklist::fastCheck(const ACLList * list) +ACLChecklist::fastCheck(const Acl::Tree * list) { PROF_start(aclCheckFast); preCheck("fast ACLs"); + asyncCaller_ = false; + + // This call is not compatible with a pre-set accessList because we cannot + // tell whether this Checklist is used by some other concurent call, which + // is not supported. + assert(!accessList); + accessList = list; // assume DENY/ALLOW on mis/matches due to not having acl_access object - if (matchAclList(list, true)) - markFinished(ACCESS_ALLOWED, "all ACLs matched"); - else if (!finished()) - markFinished(ACCESS_DENIED, "ACL mismatched"); + // matchAndFinish() takes care of the ALLOW case + cbdataReference(accessList); // required for cbdataReferenceValid() + if (accessList && cbdataReferenceValid(accessList)) + matchAndFinish(); // calls markFinished() on success + if (!finished()) + markFinished(ACCESS_DENIED, "ACLs failed to match"); + + cbdataReferenceDone(accessList); PROF_stop(aclCheckFast); return currentAnswer(); } @@ -409,14 +325,12 @@ ACLChecklist::fastCheck() PROF_start(aclCheckFast); preCheck("fast rules"); + asyncCaller_ = false; - allow_t lastSeenKeyword = ACCESS_DUNNO; debugs(28, 5, "aclCheckFast: list: " << accessList); - const acl_access *acl = cbdataReference(accessList); - while (acl != NULL && cbdataReferenceValid(acl)) { - // on a match, finish - if (matchAclList(acl->aclList, true)) - markFinished(acl->allow, "first matching rule won"); + const Acl::Tree *acl = cbdataReference(accessList); + if (acl != NULL && cbdataReferenceValid(acl)) { + matchAndFinish(); // calls markFinished() on success // if finished (on a match or in exceptional cases), stop if (finished()) { @@ -425,15 +339,12 @@ ACLChecklist::fastCheck() return currentAnswer(); } - // on a mismatch, try the next access rule - lastSeenKeyword = acl->allow; - const acl_access *A = acl; - acl = cbdataReference(acl->next); - cbdataReferenceDone(A); + // fall through for mismatch handling } // There were no rules to match or no rules matched - calcImplicitAnswer(lastSeenKeyword); + calcImplicitAnswer(); + cbdataReferenceDone(acl); PROF_stop(aclCheckFast); return currentAnswer(); @@ -443,8 +354,11 @@ ACLChecklist::fastCheck() /// action (or ACCESS_DUNNO if the reversal is not possible). The caller /// should set lastSeenAction to ACCESS_DUNNO if there were no rules to see. void -ACLChecklist::calcImplicitAnswer(const allow_t &lastSeenAction) +ACLChecklist::calcImplicitAnswer() { + // XXX: rename lastSeenAction after review and before commit + const allow_t lastSeenAction = (accessList && cbdataReferenceValid(accessList)) ? + accessList->lastAction() : allow_t(ACCESS_DUNNO); allow_t implicitRuleAnswer = ACCESS_DUNNO; if (lastSeenAction == ACCESS_DENIED) // reverse last seen "deny" implicitRuleAnswer = ACCESS_ALLOWED; @@ -457,18 +371,6 @@ ACLChecklist::calcImplicitAnswer(const allow_t &lastSeenAction) markFinished(implicitRuleAnswer, "implicit rule won"); } -bool -ACLChecklist::checking() const -{ - return checking_; -} - -void -ACLChecklist::checking (bool const newValue) -{ - checking_ = newValue; -} - bool ACLChecklist::callerGone() { diff --git a/src/acl/Checklist.h b/src/acl/Checklist.h index 8efdf87520..ee56f90ed7 100644 --- a/src/acl/Checklist.h +++ b/src/acl/Checklist.h @@ -31,7 +31,8 @@ #ifndef SQUID_ACLCHECKLIST_H #define SQUID_ACLCHECKLIST_H -#include "acl/Acl.h" +#include "acl/InnerNode.h" +#include /// ACL checklist callback typedef void ACLCB(allow_t, void *); @@ -67,9 +68,6 @@ public: public: virtual void checkForAsync(ACLChecklist *) const = 0; virtual ~AsyncState() {} - - protected: - void changeState (ACLChecklist *, AsyncState *) const; }; class NullState : public AsyncState @@ -153,25 +151,29 @@ public: * * If there are no ACLs to check at all, the result becomes ACCESS_ALLOWED. */ - allow_t const & fastCheck(const ACLList * list); + allow_t const & fastCheck(const Acl::Tree *list); + + /// If slow lookups are allowed, switches into "async in progress" state. + /// Otherwise, returns false; the caller is expected to handle the failure. + bool goAsync(AsyncState *); + + /// Matches (or resumes matching of) a child node while maintaning + /// resumption breadcrumbs if a [grand]child node goes async. + bool matchChild(const Acl::InnerNode *parent, Acl::Nodes::const_iterator pos, const ACL *child); - // whether the last checked ACL of the current rule needs - // an async operation to determine whether there was a match - bool asyncNeeded() const; - bool asyncInProgress() const; - void asyncInProgress(bool const); + /// Whether we should continue to match tree nodes or stop/pause. + bool keepMatching() const { return !finished() && !asyncInProgress(); } /// whether markFinished() was called - bool finished() const; + bool finished() const { return finished_; } + /// async call has been started and has not finished (or failed) yet + bool asyncInProgress() const { return asyncStage_ != asyncNone; } /// called when no more ACLs should be checked; sets the final answer and /// prints a debugging message explaining the reason for that answer void markFinished(const allow_t &newAnswer, const char *reason); const allow_t ¤tAnswer() const { return allow_; } - void changeState(AsyncState *); - AsyncState *asyncState() const; - // XXX: ACLs that need request or reply have to use ACLFilledChecklist and // should do their own checks so that we do not have to povide these two // for ACL::checklistMatches to use @@ -182,46 +184,58 @@ private: /// Calls non-blocking check callback with the answer and destroys self. void checkCallback(allow_t answer); - void checkAccessList(); - void checkForAsync(); + void matchAndFinish(); + + void changeState(AsyncState *); + AsyncState *asyncState() const; public: - const acl_access *accessList; + const Acl::Tree *accessList; ACLCB *callback; void *callback_data; - /** - * Performs non-blocking check starting with the current rule. - * Used by nonBlockingCheck() to initiate the checks and by - * async operation callbacks to resume checks after the async - * operation updates the current Squid state. See nonBlockingCheck() - * for details on final result determination. - */ - void matchNonBlocking(); + /// Resumes non-blocking check started by nonBlockingCheck() and + /// suspended until some async operation updated Squid state. + void resumeNonBlockingCheck(AsyncState *state); private: /* internal methods */ + /// Position of a child node within an ACL tree. + class Breadcrumb { + public: + Breadcrumb(): parent(NULL) {} + Breadcrumb(const Acl::InnerNode *aParent, Acl::Nodes::const_iterator aPos): parent(aParent), position(aPos) {} + 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 = NULL; } + const Acl::InnerNode *parent; ///< intermediate node in the ACL tree + Acl::Nodes::const_iterator position; ///< child position inside parent + }; + /// possible outcomes when trying to match a single ACL node in a list typedef enum { nmrMatch, nmrMismatch, nmrFinished, nmrNeedsAsync } NodeMatchingResult; /// prepare for checking ACLs; called once per check void preCheck(const char *what); - bool matchAclList(const ACLList * list, bool const fast); - bool matchNodes(const ACLList * head, bool const fast); - NodeMatchingResult matchNode(const ACLList &node, bool const fast); - void calcImplicitAnswer(const allow_t &lastSeenAction); + bool prepNonBlocking(); + void completeNonBlocking(); + void calcImplicitAnswer(); - bool async_; + bool asyncCaller_; ///< whether the caller supports async/slow ACLs bool finished_; allow_t allow_; - AsyncState *state_; - bool checking_; - bool checking() const; - void checking (bool const); + enum AsyncStage { asyncNone, asyncStarting, asyncRunning, asyncFailed }; + AsyncStage asyncStage_; + AsyncState *state_; + Breadcrumb matchLoc_; ///< location of the node running matches() now + Breadcrumb asyncLoc_; ///< currentNode_ that called goAsync() bool callerGone(); + + /// suspended (due to an async lookup) matches() in the ACL tree + std::stack matchPath; }; #endif /* SQUID_ACLCHECKLIST_H */ diff --git a/src/acl/DestinationDomain.cc b/src/acl/DestinationDomain.cc index 82c1e3dea1..88244faec7 100644 --- a/src/acl/DestinationDomain.cc +++ b/src/acl/DestinationDomain.cc @@ -53,7 +53,6 @@ void DestinationDomainLookup::checkForAsync(ACLChecklist *cl) const { ACLFilledChecklist *checklist = Filled(cl); - checklist->asyncInProgress(true); fqdncache_nbgethostbyaddr(checklist->dst_addr, LookupDone, checklist); } @@ -61,13 +60,9 @@ void DestinationDomainLookup::LookupDone(const char *fqdn, const DnsLookupDetails &details, void *data) { ACLFilledChecklist *checklist = Filled((ACLChecklist*)data); - assert (checklist->asyncState() == DestinationDomainLookup::Instance()); - - checklist->asyncInProgress(false); - checklist->changeState (ACLChecklist::NullState::Instance()); checklist->markDestinationDomainChecked(); checklist->request->recordLookup(details); - checklist->matchNonBlocking(); + checklist->resumeNonBlockingCheck(DestinationDomainLookup::Instance()); } int @@ -112,8 +107,9 @@ ACLDestinationDomainStrategy::match (ACLData * &data, ACLFilledCheckl } else if (!checklist->destinationDomainChecked()) { /* FIXME: Using AclMatchedName here is not OO correct. Should find a way to the current acl */ debugs(28, 3, "aclMatchAcl: Can't yet compare '" << AclMatchedName << "' ACL for '" << checklist->request->GetHost() << "'"); - checklist->changeState(DestinationDomainLookup::Instance()); - return 0; + if (checklist->goAsync(DestinationDomainLookup::Instance())) + return -1; + // else fall through to "none" match, hiding the lookup failure (XXX) } return data->match("none"); diff --git a/src/acl/DestinationIp.cc b/src/acl/DestinationIp.cc index 86825b74f4..f5fb26c7a8 100644 --- a/src/acl/DestinationIp.cc +++ b/src/acl/DestinationIp.cc @@ -86,11 +86,12 @@ ACLDestinationIP::match(ACLChecklist *cl) } else if (!checklist->request->flags.destinationIpLookedUp) { /* No entry in cache, lookup not attempted */ debugs(28, 3, "aclMatchAcl: Can't yet compare '" << name << "' ACL for '" << checklist->request->GetHost() << "'"); - checklist->changeState (DestinationIPLookup::Instance()); - return 0; - } else { - return 0; + if (checklist->goAsync(DestinationIPLookup::Instance())) + return -1; + // else fall through to mismatch, hiding the lookup failure (XXX) } + + return 0; } DestinationIPLookup DestinationIPLookup::instance_; @@ -105,7 +106,6 @@ void DestinationIPLookup::checkForAsync(ACLChecklist *cl)const { ACLFilledChecklist *checklist = Filled(cl); - checklist->asyncInProgress(true); ipcache_nbgethostbyname(checklist->request->GetHost(), LookupDone, checklist); } @@ -113,12 +113,9 @@ void DestinationIPLookup::LookupDone(const ipcache_addrs *, const DnsLookupDetails &details, void *data) { ACLFilledChecklist *checklist = Filled((ACLChecklist*)data); - assert (checklist->asyncState() == DestinationIPLookup::Instance()); checklist->request->flags.destinationIpLookedUp = true; checklist->request->recordLookup(details); - checklist->asyncInProgress(false); - checklist->changeState (ACLChecklist::NullState::Instance()); - checklist->matchNonBlocking(); + checklist->resumeNonBlockingCheck(DestinationIPLookup::Instance()); } ACL * diff --git a/src/acl/FilledChecklist.h b/src/acl/FilledChecklist.h index 362711bf9f..e5470c7f7f 100644 --- a/src/acl/FilledChecklist.h +++ b/src/acl/FilledChecklist.h @@ -2,6 +2,7 @@ #define SQUID_ACLFILLED_CHECKLIST_H #include "acl/Checklist.h" +#include "acl/forward.h" #include "ip/Address.h" #if USE_AUTH #include "auth/UserRequest.h" diff --git a/src/acl/Gadgets.cc b/src/acl/Gadgets.cc index f6fd6ba3cb..d43f89f58c 100644 --- a/src/acl/Gadgets.cc +++ b/src/acl/Gadgets.cc @@ -41,6 +41,7 @@ #include "acl/AclNameList.h" #include "acl/AclDenyInfoList.h" #include "acl/Checklist.h" +#include "acl/Tree.h" #include "acl/Strategised.h" #include "acl/Gadgets.h" #include "ConfigParser.h" @@ -134,7 +135,7 @@ aclParseDenyInfoLine(AclDenyInfoList ** head) while ((t = strtok(NULL, w_space))) { L = (AclNameList *)memAllocate(MEM_ACL_NAME_LIST); - xstrncpy(L->name, t, ACL_NAME_SZ); + xstrncpy(L->name, t, ACL_NAME_SZ-1); *Tail = L; Tail = &L->next; } @@ -153,84 +154,87 @@ aclParseDenyInfoLine(AclDenyInfoList ** head) } void -aclParseAccessLine(ConfigParser &parser, acl_access ** head) +aclParseAccessLine(const char *directive, ConfigParser &, acl_access **treep) { - char *t = NULL; - acl_access *A = NULL; - acl_access *B = NULL; - acl_access **T = NULL; - /* first expect either 'allow' or 'deny' */ + const char *t = ConfigParser::strtokFile(); - if ((t = strtok(NULL, w_space)) == NULL) { + if (!t) { debugs(28, DBG_CRITICAL, "aclParseAccessLine: " << cfg_filename << " line " << config_lineno << ": " << config_input_line); debugs(28, DBG_CRITICAL, "aclParseAccessLine: missing 'allow' or 'deny'."); return; } - A = new acl_access; - + allow_t action = ACCESS_DUNNO; if (!strcmp(t, "allow")) - A->allow = ACCESS_ALLOWED; + action = ACCESS_ALLOWED; else if (!strcmp(t, "deny")) - A->allow = ACCESS_DENIED; + action = ACCESS_DENIED; else { debugs(28, DBG_CRITICAL, "aclParseAccessLine: " << cfg_filename << " line " << config_lineno << ": " << config_input_line); debugs(28, DBG_CRITICAL, "aclParseAccessLine: expecting 'allow' or 'deny', got '" << t << "'."); - delete A; return; } - aclParseAclList(parser, &A->aclList); + const int ruleId = ((treep && *treep) ? (*treep)->childrenCount() : 0) + 1; + MemBuf ctxBuf; + ctxBuf.init(); + ctxBuf.Printf("%s#%d", directive, ruleId); + ctxBuf.terminate(); - if (A->aclList == NULL) { - debugs(28, DBG_CRITICAL, "" << cfg_filename << " line " << config_lineno << ": " << config_input_line); + Acl::AndNode *rule = new Acl::AndNode; + rule->context(ctxBuf.content(), config_input_line); + rule->lineParse(); + if (rule->empty()) { + debugs(28, DBG_CRITICAL, "aclParseAccessLine: " << cfg_filename << " line " << config_lineno << ": " << config_input_line); debugs(28, DBG_CRITICAL, "aclParseAccessLine: Access line contains no ACL's, skipping"); - delete A; + delete rule; return; } - A->cfgline = xstrdup(config_input_line); /* Append to the end of this list */ - for (B = *head, T = head; B; T = &B->next, B = B->next); - *T = A; + assert(treep); + if (!*treep) { + *treep = new Acl::Tree; + (*treep)->context(directive, config_input_line); + } + + (*treep)->add(rule, action); /* We lock _acl_access structures in ACLChecklist::matchNonBlocking() */ } +// aclParseAclList does not expect or set actions (cf. aclParseAccessLine) void -aclParseAclList(ConfigParser &parser, ACLList ** head) +aclParseAclList(ConfigParser &, Acl::Tree **treep, const char *label) { - ACLList **Tail = head; /* sane name in the use below */ - ACL *a = NULL; - char *t; - - /* next expect a list of ACL names, possibly preceeded - * by '!' for negation */ - - while ((t = strtok(NULL, w_space))) { - ACLList *L = new ACLList; - - if (*t == '!') { - L->negated (true); - ++t; - } - - debugs(28, 3, "aclParseAclList: looking for ACL name '" << t << "'"); - a = ACL::FindByName(t); - - if (a == NULL) { - debugs(28, DBG_CRITICAL, "aclParseAclList: ACL name '" << t << "' not found."); - delete L; - parser.destruct(); - continue; - } - - L->_acl = a; - *Tail = L; - Tail = &L->next; - } + // accomodate callers unable to convert their ACL list context to string + if (!label) + label = "..."; + + MemBuf ctxLine; + ctxLine.init(); + ctxLine.Printf("(%s %s line)", cfg_directive, label); + ctxLine.terminate(); + + Acl::AndNode *rule = new Acl::AndNode; + rule->context(ctxLine.content(), config_input_line); + rule->lineParse(); + + MemBuf ctxTree; + ctxTree.init(); + ctxTree.Printf("%s %s", cfg_directive, label); + ctxTree.terminate(); + + // We want a cbdata-protected Tree (despite giving it only one child node). + Acl::Tree *tree = new Acl::Tree; + tree->add(rule); + tree->context(ctxTree.content(), config_input_line); + + assert(treep); + assert(!*treep); + *treep = tree; } /*********************/ @@ -253,32 +257,20 @@ aclDestroyAcls(ACL ** head) } void -aclDestroyAclList(ACLList ** head) +aclDestroyAclList(ACLList **list) { - ACLList *l; debugs(28, 8, "aclDestroyAclList: invoked"); - - for (l = *head; l; l = *head) { - *head = l->next; - delete l; - } + assert(list); + cbdataFree(*list); } void aclDestroyAccessList(acl_access ** list) { - acl_access *l = NULL; - acl_access *next = NULL; - - for (l = *list; l; l = next) { - debugs(28, 3, "aclDestroyAccessList: '" << l->cfgline << "'"); - next = l->next; - aclDestroyAclList(&l->aclList); - safe_free(l->cfgline); - cbdataFree(l); - } - - *list = NULL; + assert(list); + if (*list) + debugs(28, 3, "destroying: " << *list << ' ' << (*list)->name); + cbdataFree(*list); } /* maex@space.net (06.09.1996) diff --git a/src/acl/Gadgets.h b/src/acl/Gadgets.h index d59175cae2..12852a20b1 100644 --- a/src/acl/Gadgets.h +++ b/src/acl/Gadgets.h @@ -2,11 +2,8 @@ #define SQUID_ACL_GADGETS_H #include "err_type.h" +#include "acl/forward.h" -class acl_access; -class ACL; -class AclDenyInfoList; -class ACLList; class ConfigParser; class dlink_list; class StoreEntry; @@ -18,10 +15,22 @@ void aclDestroyAccessList(acl_access **list); void aclDestroyAcls(ACL **); /// \ingroup ACLAPI void aclDestroyAclList(ACLList **); -/// \ingroup ACLAPI -void aclParseAccessLine(ConfigParser &parser, acl_access **); -/// \ingroup ACLAPI -void aclParseAclList(ConfigParser &parser, 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 **); +/// 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). +/// \ingroup ACLAPI +void aclParseAclList(ConfigParser &parser, Acl::Tree **, const char *label); +/// Template to convert various context lables to strings. \ingroup ACLAPI +template +inline +void aclParseAclList(ConfigParser &parser, Acl::Tree **tree, const Any any) { + std::ostringstream buf; + buf << any; + aclParseAclList(parser, tree, buf.str().c_str()); +} + /// \ingroup ACLAPI int aclIsProxyAuth(const char *name); /// \ingroup ACLAPI diff --git a/src/acl/InnerNode.cc b/src/acl/InnerNode.cc new file mode 100644 index 0000000000..98d62dcfed --- /dev/null +++ b/src/acl/InnerNode.cc @@ -0,0 +1,110 @@ +#include "squid.h" +#include "acl/Acl.h" +#include "acl/BoolOps.h" +#include "acl/Checklist.h" +#include "acl/Gadgets.h" +#include "acl/InnerNode.h" +#include "cache_cf.h" +#include "ConfigParser.h" +#include "Debug.h" +#include "globals.h" +#include "wordlist.h" +#include + + +// "delete acl" class to use with std::for_each() in InnerNode::~InnerNode() +class AclDeleter { +public: + void operator()(ACL* acl) { + // Do not delete explicit ACLs; they are maintained by Config.aclList. + if (acl && !acl->registered) + delete acl; + } +}; + + +Acl::InnerNode::~InnerNode() +{ + std::for_each(nodes.begin(), nodes.end(), AclDeleter()); +} + +void +Acl::InnerNode::prepareForUse() +{ + std::for_each(nodes.begin(), nodes.end(), std::mem_fun(&ACL::prepareForUse)); +} + +bool +Acl::InnerNode::empty() const +{ + return nodes.empty(); +} + +void +Acl::InnerNode::add(ACL *node) +{ + assert(node != NULL); + nodes.push_back(node); +} + +// one call parses one "acl name acltype name1 name2 ..." line +// kids use this method to handle [multiple] parse() calls correctly +void +Acl::InnerNode::lineParse() +{ + // XXX: not precise, may change when looping or parsing multiple lines + if (!cfgline) + cfgline = xstrdup(config_input_line); + + // expect a list of ACL names, each possibly preceeded by '!' for negation + + while (const char *t = ConfigParser::strtokFile()) { + const bool negated = (*t == '!'); + if (negated) + ++t; + + debugs(28, 3, "looking for ACL " << t); + ACL *a = ACL::FindByName(t); + + if (a == NULL) { + debugs(28, DBG_CRITICAL, "ACL not found: " << t); + self_destruct(); + return; + } + + // append(negated ? new NotNode(a) : a); + if (negated) + add(new NotNode(a)); + else + add(a); + } + + return; +} + +wordlist* +Acl::InnerNode::dump() const +{ + wordlist *values = NULL; + for (Nodes::const_iterator i = nodes.begin(); i != nodes.end(); ++i) + wordlistAdd(&values, (*i)->name); + return values; +} + +int +Acl::InnerNode::match(ACLChecklist *checklist) +{ + return doMatch(checklist, nodes.begin()); +} + +bool +Acl::InnerNode::resumeMatchingAt(ACLChecklist *checklist, Nodes::const_iterator pos) const +{ + debugs(28, 5, "checking " << name << " at " << (pos-nodes.begin())); + const int result = doMatch(checklist, pos); + const char *extra = checklist->asyncInProgress() ? " async" : ""; + debugs(28, 3, "checked: " << name << " = " << result << extra); + + // merges async and failures (-1) into "not matched" + return result == 1; +} diff --git a/src/acl/InnerNode.h b/src/acl/InnerNode.h new file mode 100644 index 0000000000..832ab73556 --- /dev/null +++ b/src/acl/InnerNode.h @@ -0,0 +1,47 @@ +#ifndef SQUID_ACL_INNER_NODE_H +#define SQUID_ACL_INNER_NODE_H + +#include "acl/Acl.h" +#include + +namespace Acl { + +typedef std::vector Nodes; ///< a collection of nodes + +/// An intermediate ACL tree node. Manages a collection of child tree nodes. +class InnerNode: public ACL +{ +public: + virtual ~InnerNode(); + + /// Resumes matching (suspended by an async call) at the given position. + bool resumeMatchingAt(ACLChecklist *checklist, Acl::Nodes::const_iterator pos) const; + + /// the number of children nodes + Nodes::size_type childrenCount() const { return nodes.size(); } + + /* ACL API */ + virtual void prepareForUse(); + virtual bool empty() const; + virtual wordlist *dump() const; + + /// parses one "acl name type acl1 acl2..." line, appending to nodes + void lineParse(); + + /// appends the node to the collection and takes control over it + void add(ACL *node); + +protected: + /// checks whether the nodes match, starting with the given one + /// kids determine what a match means for their type of intermediate nodes + virtual int doMatch(ACLChecklist *checklist, Nodes::const_iterator start) const = 0; + + /* ACL API */ + virtual int match(ACLChecklist *checklist); + + std::vector nodes; ///< children nodes of this intermediate node +}; + +} // namespace Acl + +#endif /* SQUID_ACL_INNER_NODE_H */ diff --git a/src/acl/Makefile.am b/src/acl/Makefile.am index 3a1d122766..71d2b12fcb 100644 --- a/src/acl/Makefile.am +++ b/src/acl/Makefile.am @@ -8,7 +8,14 @@ libapi_la_SOURCES = \ Acl.cc \ Acl.h \ Checklist.cc \ - Checklist.h + Checklist.h \ + forward.h \ + InnerNode.cc \ + InnerNode.h \ + BoolOps.cc \ + BoolOps.h \ + Tree.cc \ + Tree.h ## Data-dependent Squid/transaction state used by specific ACLs. ## Does not refer to specific ACLs to avoid circular dependencies. @@ -36,6 +43,10 @@ libacls_la_SOURCES = \ TimeData.h \ Asn.cc \ Asn.h \ + AllOf.cc \ + AllOf.h \ + AnyOf.cc \ + AnyOf.h \ Browser.cc \ Browser.h \ DestinationAsn.h \ diff --git a/src/acl/SourceDomain.cc b/src/acl/SourceDomain.cc index dced292ccf..a68ed988d8 100644 --- a/src/acl/SourceDomain.cc +++ b/src/acl/SourceDomain.cc @@ -51,7 +51,6 @@ SourceDomainLookup::Instance() void SourceDomainLookup::checkForAsync(ACLChecklist *checklist) const { - checklist->asyncInProgress(true); fqdncache_nbgethostbyaddr(Filled(checklist)->src_addr, LookupDone, checklist); } @@ -59,13 +58,9 @@ void SourceDomainLookup::LookupDone(const char *fqdn, const DnsLookupDetails &details, void *data) { ACLFilledChecklist *checklist = Filled((ACLChecklist*)data); - assert (checklist->asyncState() == SourceDomainLookup::Instance()); - - checklist->asyncInProgress(false); - checklist->changeState (ACLChecklist::NullState::Instance()); checklist->markSourceDomainChecked(); checklist->request->recordLookup(details); - checklist->matchNonBlocking(); + checklist->resumeNonBlockingCheck(SourceDomainLookup::Instance()); } int @@ -79,8 +74,9 @@ ACLSourceDomainStrategy::match (ACLData * &data, ACLFilledChecklist * } else if (!checklist->sourceDomainChecked()) { /* FIXME: Using AclMatchedName here is not OO correct. Should find a way to the current acl */ debugs(28, 3, "aclMatchAcl: Can't yet compare '" << AclMatchedName << "' ACL for '" << checklist->src_addr << "'"); - checklist->changeState(SourceDomainLookup::Instance()); - return 0; + if (checklist->goAsync(SourceDomainLookup::Instance())) + return -1; + // else fall through to "none" match, hiding the lookup failure (XXX) } return data->match("none"); diff --git a/src/acl/Tree.cc b/src/acl/Tree.cc new file mode 100644 index 0000000000..552948f663 --- /dev/null +++ b/src/acl/Tree.cc @@ -0,0 +1,76 @@ +#include "squid.h" +#include "acl/Tree.h" +#include "wordlist.h" + +CBDATA_NAMESPACED_CLASS_INIT(Acl, Tree); + + +allow_t +Acl::Tree::winningAction() const +{ + return actionAt(lastMatch_ - nodes.begin()); +} + +allow_t +Acl::Tree::lastAction() const +{ + if (actions.empty()) + return ACCESS_DUNNO; + return actions.back(); +} + +/// computes action that corresponds to the position of the matched rule +allow_t +Acl::Tree::actionAt(const Nodes::size_type pos) const +{ + assert(0 <= pos && pos < nodes.size()); + if (actions.size()) { + assert(actions.size() == nodes.size()); + return actions[pos]; + } + // default for matched rules in trees without actions + return ACCESS_ALLOWED; +} + +void +Acl::Tree::add(ACL *rule, const allow_t &action) +{ + // either all rules have actions or none + assert(nodes.size() == actions.size()); + InnerNode::add(rule); + actions.push_back(action); +} + +void +Acl::Tree::add(ACL *rule) +{ + // either all rules have actions or none + assert(actions.empty()); + InnerNode::add(rule); +} + +wordlist* +Acl::Tree::treeDump(const char *prefix, const ActionToString &convert) const +{ + wordlist *text = NULL; + Actions::const_iterator action = actions.begin(); + typedef Nodes::const_iterator NCI; + for (NCI node = nodes.begin(); node != nodes.end(); ++node) { + + wordlistAdd(&text, prefix); + + if (action != actions.end()) { + const char *act = convert ? convert[action->kind] : + (*action == ACCESS_ALLOWED ? "Allow" : "Deny"); + wordlistAdd(&text, act ? act : "???"); + ++action; + } + + wordlist *rule = (*node)->dump(); + wordlistAddWl(&text, rule); + wordlistDestroy(&rule); + + wordlistAdd(&text, "\n"); + } + return text; +} diff --git a/src/acl/Tree.h b/src/acl/Tree.h new file mode 100644 index 0000000000..c244dafee9 --- /dev/null +++ b/src/acl/Tree.h @@ -0,0 +1,44 @@ +#ifndef SQUID_ACL_TREE_H +#define SQUID_ACL_TREE_H + +#include "acl/BoolOps.h" + +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. +class Tree: public OrNode +{ +public: + /// dumps tuples + /// action.kind is mapped to a string using the supplied conversion table + typedef const char **ActionToString; + wordlist* treeDump(const char *name, const ActionToString &convert) const; + + /// Returns the corresponding action after a successful tree match. + allow_t winningAction() const; + + /// what action to use if no nodes matched + allow_t lastAction() const; + + /// appends and takes control over the rule with a given action + void add(ACL *rule, const allow_t &action); + void add(ACL *rule); ///< same as InnerNode::add() + +protected: + allow_t actionAt(const Nodes::size_type pos) const; + + /// if not empty, contains actions corresponding to InnerNode::nodes + typedef std::vector Actions; + Actions actions; + +private: + // XXX: We should use refcounting instead, but it requires making ACLs + // refcounted as well. Otherwise, async lookups will reach deleted ACLs. + CBDATA_CLASS2(Tree); +}; + + +} // namespace Acl + +#endif /* SQUID_ACL_TREE_H */ diff --git a/src/acl/forward.h b/src/acl/forward.h new file mode 100644 index 0000000000..4c5f39b216 --- /dev/null +++ b/src/acl/forward.h @@ -0,0 +1,30 @@ +#ifndef SQUID_ACL_FORWARD_H +#define SQUID_ACL_FORWARD_H + +class ACL; +class ACLChecklist; +class ACLFilledChecklist; +class ACLList; + +class AclAddress; +class AclDenyInfoList; +class AclSizeLimit; + + +namespace Acl { + +class InnerNode; +class NotNode; +class AndNode; +class OrNode; +class Tree; + +} // namespace Acl + +#define ACL_NAME_SZ 64 + +// XXX: remove after review and before commit, after renaming all users? +#define acl_access Acl::Tree +#define ACLList Acl::Tree + +#endif /* SQUID_ACL_FORWARD_H */ diff --git a/src/adaptation/AccessRule.cc b/src/adaptation/AccessRule.cc index 28cd2832b7..8331656aa1 100644 --- a/src/adaptation/AccessRule.cc +++ b/src/adaptation/AccessRule.cc @@ -20,7 +20,7 @@ Adaptation::AccessRule::~AccessRule() void Adaptation::AccessRule::parse(ConfigParser &parser) { - aclParseAccessLine(parser, &acl); + aclParseAccessLine("adaptation_access", parser, &acl); } void diff --git a/src/adaptation/AccessRule.h b/src/adaptation/AccessRule.h index 09ad60f53f..9fae4ded79 100644 --- a/src/adaptation/AccessRule.h +++ b/src/adaptation/AccessRule.h @@ -1,10 +1,10 @@ #ifndef SQUID_ADAPTATION__ACCESS_RULE_H #define SQUID_ADAPTATION__ACCESS_RULE_H -#include "SquidString.h" +#include "acl/forward.h" #include "adaptation/forward.h" +#include "SquidString.h" -class acl_access; class ConfigParser; namespace Adaptation diff --git a/src/adaptation/Config.h b/src/adaptation/Config.h index 65061a325c..478ce321c3 100644 --- a/src/adaptation/Config.h +++ b/src/adaptation/Config.h @@ -2,6 +2,7 @@ #define SQUID_ADAPTATION__CONFIG_H #include "event.h" +#include "acl/forward.h" #include "acl/Gadgets.h" #include "base/AsyncCall.h" #include "adaptation/forward.h" @@ -9,7 +10,6 @@ #include "Notes.h" #include "SquidString.h" -class acl_access; class ConfigParser; class HttpRequest; class HttpReply; diff --git a/src/adaptation/icap/Config.h b/src/adaptation/icap/Config.h index f0ae4feaaa..012efb7e43 100644 --- a/src/adaptation/icap/Config.h +++ b/src/adaptation/icap/Config.h @@ -36,11 +36,10 @@ #include "event.h" #include "base/AsyncCall.h" +#include "acl/forward.h" #include "adaptation/Config.h" #include "adaptation/icap/ServiceRep.h" -class acl_access; - namespace Adaptation { namespace Icap diff --git a/src/auth/Acl.cc b/src/auth/Acl.cc index e388d68496..024db72269 100644 --- a/src/auth/Acl.cc +++ b/src/auth/Acl.cc @@ -58,8 +58,10 @@ AuthenticateAcl(ACLChecklist *ch) break; case AUTH_ACL_HELPER: - debugs(28, 4, HERE << "returning " << ACCESS_DUNNO << " sending credentials to helper."); - checklist->changeState(ProxyAuthLookup::Instance()); + if (checklist->goAsync(ProxyAuthLookup::Instance())) + debugs(28, 4, "returning " << ACCESS_DUNNO << " sending credentials to helper."); + else + debugs(28, 2, "cannot go async; returning " << ACCESS_DUNNO); return ACCESS_DUNNO; // XXX: break this down into DUNNO, EXPIRED_OK, EXPIRED_BAD states case AUTH_ACL_CHALLENGE: diff --git a/src/auth/AclMaxUserIp.cc b/src/auth/AclMaxUserIp.cc index e297ddad12..1b197c0e1c 100644 --- a/src/auth/AclMaxUserIp.cc +++ b/src/auth/AclMaxUserIp.cc @@ -139,8 +139,8 @@ ACLMaxUserIP::match(ACLChecklist *cl) case ACCESS_AUTH_REQUIRED: default: // If the answer is not allowed or denied (matches/not matches) and - // async authentication is not needed (asyncNeeded), then we are done. - if (!checklist->asyncNeeded()) + // async authentication is not in progress, then we are done. + if (checklist->keepMatching()) checklist->markFinished(answer, "AuthenticateAcl exception"); return -1; // other } diff --git a/src/auth/AclProxyAuth.cc b/src/auth/AclProxyAuth.cc index 60d17ccba6..2f2dbd30b9 100644 --- a/src/auth/AclProxyAuth.cc +++ b/src/auth/AclProxyAuth.cc @@ -92,8 +92,8 @@ ACLProxyAuth::match(ACLChecklist *checklist) case ACCESS_AUTH_REQUIRED: default: // If the answer is not allowed or denied (matches/not matches) and - // async authentication is not needed (asyncNeeded), then we are done. - if (!checklist->asyncNeeded()) + // async authentication is not in progress, then we are done. + if (checklist->keepMatching()) checklist->markFinished(answer, "AuthenticateAcl exception"); return -1; // other } @@ -140,7 +140,6 @@ ProxyAuthLookup::checkForAsync(ACLChecklist *cl)const { ACLFilledChecklist *checklist = Filled(cl); - checklist->asyncInProgress(true); debugs(28, 3, HERE << "checking password via authenticator"); /* make sure someone created auth_user_request for us */ @@ -154,8 +153,6 @@ ProxyAuthLookup::LookupDone(void *data) { ACLFilledChecklist *checklist = Filled(static_cast(data)); - assert (checklist->asyncState() == ProxyAuthLookup::Instance()); - if (checklist->auth_user_request == NULL || !checklist->auth_user_request->valid() || checklist->conn() == NULL) { /* credentials could not be checked either way * restart the whole process */ @@ -167,9 +164,7 @@ ProxyAuthLookup::LookupDone(void *data) } } - checklist->asyncInProgress(false); - checklist->changeState (ACLChecklist::NullState::Instance()); - checklist->matchNonBlocking(); + checklist->resumeNonBlockingCheck(ProxyAuthLookup::Instance()); } ACL * diff --git a/src/cache_cf.cc b/src/cache_cf.cc index 9485c8eca2..ccb64d54d4 100644 --- a/src/cache_cf.cc +++ b/src/cache_cf.cc @@ -38,6 +38,7 @@ #include "acl/AclSizeLimit.h" #include "acl/Gadgets.h" #include "acl/MethodData.h" +#include "acl/Tree.h" #include "anyp/PortCfg.h" #include "AuthReg.h" #include "base/RunnersRegistry.h" @@ -1287,11 +1288,15 @@ parseBytesUnits(const char *unit) *****************************************************************************/ static void -dump_acl(StoreEntry * entry, const char *name, ACL * ae) +dump_wordlist(StoreEntry * entry, wordlist *words) { - wordlist *w; - wordlist *v; + for (wordlist *word = words; word; word = words->next) + storeAppendPrintf(entry, "%s ", word->key); +} +static void +dump_acl(StoreEntry * entry, const char *name, ACL * ae) +{ while (ae != NULL) { debugs(3, 3, "dump_acl: " << name << " " << ae->name); storeAppendPrintf(entry, "%s %s %s %s ", @@ -1299,13 +1304,8 @@ dump_acl(StoreEntry * entry, const char *name, ACL * ae) ae->name, ae->typeString(), ae->flags.flagsStr()); - v = w = ae->dump(); - - while (v != NULL) { - debugs(3, 3, "dump_acl: " << name << " " << ae->name << " " << v->key); - storeAppendPrintf(entry, "%s ", v->key); - v = v->next; - } + wordlist *w = ae->dump(); + dump_wordlist(entry, w); storeAppendPrintf(entry, "\n"); wordlistDestroy(&w); @@ -1328,33 +1328,29 @@ free_acl(ACL ** ae) void dump_acl_list(StoreEntry * entry, ACLList * head) { - ACLList *l; - - for (l = head; l; l = l->next) { - storeAppendPrintf(entry, " %s%s", - l->op ? null_string : "!", - l->_acl->name); - } + wordlist *values = head->dump(); + dump_wordlist(entry, values); + wordlistDestroy(&values); } void dump_acl_access(StoreEntry * entry, const char *name, acl_access * head) { - acl_access *l; - - for (l = head; l; l = l->next) { - storeAppendPrintf(entry, "%s %s", - name, - l->allow ? "Allow" : "Deny"); - dump_acl_list(entry, l->aclList); - storeAppendPrintf(entry, "\n"); - } + wordlist *lines = head->treeDump(name, NULL); + dump_wordlist(entry, lines); + wordlistDestroy(&lines); } static void parse_acl_access(acl_access ** head) { - aclParseAccessLine(LegacyParser, head); + aclParseAccessLine(cfg_directive, LegacyParser, head); +} + +static void +parse_acl_access(const char *directive, acl_access ** head) +{ + aclParseAccessLine(directive, LegacyParser, head); } static void @@ -1431,7 +1427,7 @@ parse_acl_address(AclAddress ** head) CBDATA_INIT_TYPE_FREECB(AclAddress, freed_acl_address); l = cbdataAlloc(AclAddress); parse_address(&l->addr); - aclParseAclList(LegacyParser, &l->aclList); + aclParseAclList(LegacyParser, &l->aclList, l->addr); while (*tail) tail = &(*tail)->next; @@ -1499,7 +1495,7 @@ parse_acl_tos(acl_tos ** head) l->tos = (tos_t)tos; - aclParseAclList(LegacyParser, &l->aclList); + aclParseAclList(LegacyParser, &l->aclList, token); while (*tail) tail = &(*tail)->next; @@ -1570,7 +1566,7 @@ parse_acl_nfmark(acl_nfmark ** head) l->nfmark = mark; - aclParseAclList(LegacyParser, &l->aclList); + aclParseAclList(LegacyParser, &l->aclList, token); while (*tail) tail = &(*tail)->next; @@ -1628,7 +1624,7 @@ parse_acl_b_size_t(AclSizeLimit ** head) parse_b_int64_t(&l->size); - aclParseAclList(LegacyParser, &l->aclList); + aclParseAclList(LegacyParser, &l->aclList, l->size); while (*tail) tail = &(*tail)->next; @@ -1765,7 +1761,10 @@ parse_http_header_access(HeaderManglers **pm) HeaderManglers *manglers = *pm; headerMangler *mangler = manglers->track(t); assert(mangler); - parse_acl_access(&mangler->access_list); + + std::string directive = "http_header_access "; + directive += t; + parse_acl_access(directive.c_str(), &mangler->access_list); } static void @@ -2528,7 +2527,9 @@ parse_peer_access(void) return; } - aclParseAccessLine(LegacyParser, &p->access); + std::string directive = "peer_access "; + directive += host; + aclParseAccessLine(directive.c_str(), LegacyParser, &p->access); } static void @@ -4038,7 +4039,7 @@ parse_access_log(CustomLog ** logs) if (strcmp(filename, "none") == 0) { cl->type = Log::Format::CLF_NONE; - aclParseAclList(LegacyParser, &cl->aclList); + aclParseAclList(LegacyParser, &cl->aclList, filename); while (*logs) logs = &(*logs)->next; *logs = cl; @@ -4090,7 +4091,7 @@ parse_access_log(CustomLog ** logs) return; } - aclParseAclList(LegacyParser, &cl->aclList); + aclParseAclList(LegacyParser, &cl->aclList, filename); while (*logs) logs = &(*logs)->next; @@ -4432,7 +4433,7 @@ static void parse_sslproxy_cert_adapt(sslproxy_cert_adapt **cert_adapt) return; } - aclParseAclList(LegacyParser, &ca->aclList); + aclParseAclList(LegacyParser, &ca->aclList, al); while (*cert_adapt) cert_adapt = &(*cert_adapt)->next; @@ -4486,7 +4487,7 @@ static void parse_sslproxy_cert_sign(sslproxy_cert_sign **cert_sign) return; } - aclParseAclList(LegacyParser, &cs->aclList); + aclParseAclList(LegacyParser, &cs->aclList, al); while (*cert_sign) cert_sign = &(*cert_sign)->next; @@ -4572,31 +4573,30 @@ static void parse_sslproxy_ssl_bump(acl_access **ssl_bump) sslBumpCfgRr::lastDeprecatedRule = Ssl::bumpEnd; } - acl_access *A = new acl_access; - A->allow = allow_t(ACCESS_ALLOWED); + allow_t action = allow_t(ACCESS_ALLOWED); if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpClientFirst]) == 0) { - A->allow.kind = Ssl::bumpClientFirst; + action.kind = Ssl::bumpClientFirst; bumpCfgStyleNow = bcsNew; } else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpServerFirst]) == 0) { - A->allow.kind = Ssl::bumpServerFirst; + action.kind = Ssl::bumpServerFirst; bumpCfgStyleNow = bcsNew; } else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpNone]) == 0) { - A->allow.kind = Ssl::bumpNone; + action.kind = Ssl::bumpNone; bumpCfgStyleNow = bcsNew; } else if (strcmp(bm, "allow") == 0) { debugs(3, DBG_CRITICAL, "SECURITY NOTICE: auto-converting deprecated " "\"ssl_bump allow \" to \"ssl_bump client-first \" which " "is usually inferior to the newer server-first " "bumping mode. Update your ssl_bump rules."); - A->allow.kind = Ssl::bumpClientFirst; + action.kind = Ssl::bumpClientFirst; bumpCfgStyleNow = bcsOld; sslBumpCfgRr::lastDeprecatedRule = Ssl::bumpClientFirst; } else if (strcmp(bm, "deny") == 0) { debugs(3, DBG_CRITICAL, "WARNING: auto-converting deprecated " "\"ssl_bump deny \" to \"ssl_bump none \". Update " "your ssl_bump rules."); - A->allow.kind = Ssl::bumpNone; + action.kind = Ssl::bumpNone; bumpCfgStyleNow = bcsOld; sslBumpCfgRr::lastDeprecatedRule = Ssl::bumpNone; } else { @@ -4614,22 +4614,26 @@ static void parse_sslproxy_ssl_bump(acl_access **ssl_bump) bumpCfgStyleLast = bumpCfgStyleNow; - aclParseAclList(LegacyParser, &A->aclList); + ACL *rule = new Acl::AndNode; + rule->parse(); + // empty rule OK + rule->context("(ssl_bump rule)", config_input_line); + + assert(ssl_bump); + if (!*ssl_bump) { + *ssl_bump = new Acl::Tree; + (*ssl_bump)->context("(ssl_bump rules)", config_input_line); + } - acl_access *B, **T; - for (B = *ssl_bump, T = ssl_bump; B; T = &B->next, B = B->next); - *T = A; + (*ssl_bump)->add(rule, action); } static void dump_sslproxy_ssl_bump(StoreEntry *entry, const char *name, acl_access *ssl_bump) { - acl_access *sb; - for (sb = ssl_bump; sb != NULL; sb = sb->next) { - storeAppendPrintf(entry, "%s ", name); - storeAppendPrintf(entry, "%s ", Ssl::bumpMode(sb->allow.kind)); - if (sb->aclList) - dump_acl_list(entry, sb->aclList); - storeAppendPrintf(entry, "\n"); + if (ssl_bump) { + wordlist *lines = ssl_bump->treeDump(name, Ssl::BumpModeStr); + dump_wordlist(entry, lines); + wordlistDestroy(&lines); } } @@ -4683,7 +4687,8 @@ static void parse_HeaderWithAclList(HeaderWithAclList **headers) } hwa.valueFormat = nlf; } - aclParseAclList(LegacyParser, &hwa.aclList); + + aclParseAclList(LegacyParser, &hwa.aclList, (hwa.fieldName + ':' + hwa.fieldValue).c_str()); (*headers)->push_back(hwa); } diff --git a/src/cf.data.pre b/src/cf.data.pre index 7ad9637f5e..21a1aac86d 100644 --- a/src/cf.data.pre +++ b/src/cf.data.pre @@ -1076,6 +1076,29 @@ IF USE_SSL # The SHA1 digest algorithm is the default and is currently # the only algorithm supported (-sha1). ENDIF + acl aclname any-of acl1 acl2 ... + # match any one of the acls [fast or slow] + # The first matching ACL stops further ACL evaluation. + # + # ACLs from multiple any-of lines with the same name are ORed. + # For example, A = (a1 or a2) or (a3 or a4) can be written as + # acl A any-of a1 a2 + # acl A any-of a3 a4 + # + # This group ACL is fast if all evaluated ACLs in the group are fast + # and slow otherwise. + + acl aclname all-of acl1 acl2 ... + # match all of the acls [fast or slow] + # The first mismatching ACL stops further ACL evaluation. + # + # ACLs from multiple all-of lines with the same name are ORed. + # For example, B = (b1 and b2) or (b3 and b4) can be written as + # acl B all-of b1 b2 + # acl B all-of b3 b4 + # + # This group ACL is fast if all evaluated ACLs in the group are fast + # and slow otherwise. Examples: acl macaddress arp 09:00:2b:23:45:67 diff --git a/src/cf_gen.cc b/src/cf_gen.cc index b19b97452c..7d9e5591d1 100644 --- a/src/cf_gen.cc +++ b/src/cf_gen.cc @@ -631,6 +631,7 @@ Entry::genParseAlias(const std::string &aName, std::ostream &fout) const fout << " if (!strcmp(token, \"" << aName << "\")) {" << std::endl; if (ifdef.size()) fout << "#if " << ifdef << std::endl; + fout << " cfg_directive = \"" << aName << "\";" << std::endl; fout << " "; if (type.compare("obsolete") == 0) { fout << "debugs(0, DBG_CRITICAL, \"ERROR: Directive '" << aName << "' is obsolete.\");\n"; @@ -645,6 +646,7 @@ Entry::genParseAlias(const std::string &aName, std::ostream &fout) const fout << "parse_" << type << "(&" << loc << (array_flag ? "[0]" : "") << ");"; } fout << std::endl; + fout << " cfg_directive = NULL;" << std::endl; if (ifdef.size()) { fout << "#else" << std::endl << diff --git a/src/client_side_request.cc b/src/client_side_request.cc index 97f858d39d..237c0de8fc 100644 --- a/src/client_side_request.cc +++ b/src/client_side_request.cc @@ -780,8 +780,7 @@ ClientRequestContext::clientAccessCheckDone(const allow_t &answer) debugs(85, 2, "The request " << RequestMethodStr(http->request->method) << " " << http->uri << " is " << answer << - ", because it matched '" << - (AclMatchedName ? AclMatchedName : "NO ACL's") << "'" ); + "; last ACL checked: " << (AclMatchedName ? AclMatchedName : "[none]")); #if USE_AUTH char const *proxy_auth_msg = ""; diff --git a/src/client_side_request.h b/src/client_side_request.h index eaa5f9211e..53d5ac7e73 100644 --- a/src/client_side_request.h +++ b/src/client_side_request.h @@ -30,6 +30,7 @@ #ifndef SQUID_CLIENTSIDEREQUEST_H #define SQUID_CLIENTSIDEREQUEST_H +#include "acl/forward.h" #include "HttpHeader.h" #include "clientStream.h" #include "client_side.h" @@ -45,8 +46,6 @@ class HttpMsg; #endif -class acl_access; -class ACLFilledChecklist; class ClientRequestContext; class ConnStateData; class MemObject; diff --git a/src/defines.h b/src/defines.h index 1828701ddd..890c5c4680 100644 --- a/src/defines.h +++ b/src/defines.h @@ -41,7 +41,6 @@ #define BUFSIZ 4096 /* make unreasonable guess */ #endif -#define ACL_NAME_SZ 32 #define BROWSERNAMELEN 128 #define ACL_SUNDAY 0x01 diff --git a/src/external_acl.cc b/src/external_acl.cc index c6a5b6f376..2b3b73d6ad 100644 --- a/src/external_acl.cc +++ b/src/external_acl.cc @@ -777,14 +777,17 @@ copyResultsFromEntry(HttpRequest *req, external_acl_entry *entry) static allow_t aclMatchExternal(external_acl_data *acl, ACLFilledChecklist *ch) { - const char *key = ""; debugs(82, 9, HERE << "acl=\"" << acl->def->name << "\""); external_acl_entry *entry = ch->extacl_entry; + external_acl_message = "MISSING REQUIRED INFORMATION"; + if (entry) { if (cbdataReferenceValid(entry) && entry->def == acl->def) { /* Ours, use it.. if the key matches */ - key = makeExternalAclKey(ch, acl); + const char *key = makeExternalAclKey(ch, acl); + if (!key) + return ACCESS_DUNNO; // insufficent data to continue if (strcmp(key, (char*)entry->key) != 0) { debugs(82, 9, HERE << "entry key='" << (char *)entry->key << "', our key='" << key << "' dont match. Discarded."); // too bad. need a new lookup. @@ -796,7 +799,7 @@ aclMatchExternal(external_acl_data *acl, ACLFilledChecklist *ch) debugs(82, 9, HERE << "entry " << entry << " not valid or not ours. Discarded."); if (entry) { debugs(82, 9, HERE << "entry def=" << entry->def << ", our def=" << acl->def); - key = makeExternalAclKey(ch, acl); + const char *key = makeExternalAclKey(ch, acl); // may be nil debugs(82, 9, HERE << "entry key='" << (char *)entry->key << "', our key='" << key << "'"); } cbdataReferenceDone(ch->extacl_entry); @@ -804,8 +807,6 @@ aclMatchExternal(external_acl_data *acl, ACLFilledChecklist *ch) } } - external_acl_message = "MISSING REQUIRED INFORMATION"; - if (!entry) { debugs(82, 9, HERE << "No helper entry available"); #if USE_AUTH @@ -820,7 +821,7 @@ aclMatchExternal(external_acl_data *acl, ACLFilledChecklist *ch) debugs(82, 3, HERE << acl->def->name << " user is authenticated."); } #endif - key = makeExternalAclKey(ch, acl); + const char *key = makeExternalAclKey(ch, acl); if (!key) { /* Not sufficient data to process */ @@ -847,7 +848,8 @@ aclMatchExternal(external_acl_data *acl, ACLFilledChecklist *ch) if (acl->def->theHelper->stats.queue_size < (int)acl->def->theHelper->childs.n_active) { debugs(82, 2, HERE << "\"" << key << "\": queueing a call."); - ch->changeState(ExternalACLLookup::Instance()); + if (!ch->goAsync(ExternalACLLookup::Instance())) + debugs(82, 2, "\"" << key << "\": no async support!"); debugs(82, 2, HERE << "\"" << key << "\": return -1."); return ACCESS_DUNNO; // expired cached or simply absent entry } else { @@ -900,8 +902,8 @@ ACLExternal::match(ACLChecklist *checklist) case ACCESS_AUTH_REQUIRED: default: // If the answer is not allowed or denied (matches/not matches) and - // async authentication is not needed (asyncNeeded), then we are done. - if (!checklist->asyncNeeded()) + // async authentication is not in progress, then we are done. + if (checklist->keepMatching()) checklist->markFinished(answer, "aclMatchExternal exception"); return -1; // other } @@ -974,7 +976,9 @@ makeExternalAclKey(ACLFilledChecklist * ch, external_acl_data * acl_data) str = ch->rfc931; if (!str || !*str) { - ch->changeState(IdentLookup::Instance()); + // if we fail to go async, we still return NULL and the caller + // will detect the failure in ACLExternal::match(). + (void)ch->goAsync(IdentLookup::Instance()); return NULL; } @@ -1392,7 +1396,7 @@ ExternalACLLookup::Start(ACLChecklist *checklist, external_acl_data *acl, bool i ACLFilledChecklist *ch = Filled(checklist); const char *key = makeExternalAclKey(ch, acl); - assert(key); + assert(key); // XXX: will fail if EXT_ACL_IDENT case needs an async lookup debugs(82, 2, HERE << (inBackground ? "bg" : "fg") << " lookup in '" << def->name << "' for '" << key << "'"); @@ -1542,7 +1546,6 @@ ExternalACLLookup::checkForAsync(ACLChecklist *checklist)const assert(acl); ACLExternal *me = dynamic_cast (acl); assert (me); - checklist->asyncInProgress(true); ACLExternal::ExternalAclLookup(checklist, me); } @@ -1552,9 +1555,7 @@ ExternalACLLookup::LookupDone(void *data, void *result) { ACLFilledChecklist *checklist = Filled(static_cast(data)); checklist->extacl_entry = cbdataReference((external_acl_entry *)result); - checklist->asyncInProgress(false); - checklist->changeState (ACLChecklist::NullState::Instance()); - checklist->matchNonBlocking(); + checklist->resumeNonBlockingCheck(ExternalACLLookup::Instance()); } /* This registers "external" in the registry. To do dynamic definitions diff --git a/src/globals.h b/src/globals.h index 211dcf84ce..2967d45eaf 100644 --- a/src/globals.h +++ b/src/globals.h @@ -47,6 +47,8 @@ extern char tmp_error_buf[ERROR_BUF_SZ]; extern char ThisCache[RFC2181_MAXHOSTNAMELEN << 1]; extern char ThisCache2[RFC2181_MAXHOSTNAMELEN << 1]; extern char config_input_line[BUFSIZ]; +/// During parsing, the name of the current squid.conf directive being parsed. +extern const char *cfg_directive; /* NULL */ extern const char *DefaultConfigFile; /* DEFAULT_CONFIG_FILE */ extern const char *cfg_filename; /* NULL */ extern const char *dash_str; /* "-" */ diff --git a/src/ident/AclIdent.cc b/src/ident/AclIdent.cc index 140eace51b..7ab36c5821 100644 --- a/src/ident/AclIdent.cc +++ b/src/ident/AclIdent.cc @@ -89,14 +89,18 @@ ACLIdent::match(ACLChecklist *cl) } else if (checklist->conn() != NULL && checklist->conn()->clientConnection != NULL && checklist->conn()->clientConnection->rfc931[0]) { return data->match(checklist->conn()->clientConnection->rfc931); } else if (checklist->conn() != NULL && Comm::IsConnOpen(checklist->conn()->clientConnection)) { - debugs(28, 3, HERE << "switching to ident lookup state"); - checklist->changeState(IdentLookup::Instance()); - return 0; + if (checklist->goAsync(IdentLookup::Instance())) { + debugs(28, 3, "switching to ident lookup state"); + return -1; + } + // else fall through to ACCESS_DUNNO failure below } else { debugs(28, DBG_IMPORTANT, HERE << "Can't start ident lookup. No client connection" ); - checklist->markFinished(ACCESS_DUNNO, "cannot start ident lookup"); - return -1; + // fall through to ACCESS_DUNNO failure below } + + checklist->markFinished(ACCESS_DUNNO, "cannot start ident lookup"); + return -1; } wordlist * @@ -133,7 +137,6 @@ IdentLookup::checkForAsync(ACLChecklist *cl)const // check that ACLIdent::match() tested this lookup precondition assert(conn && Comm::IsConnOpen(conn->clientConnection)); debugs(28, 3, HERE << "Doing ident lookup" ); - checklist->asyncInProgress(true); Ident::Start(checklist->conn()->clientConnection, LookupDone, checklist); } @@ -141,7 +144,6 @@ void IdentLookup::LookupDone(const char *ident, void *data) { ACLFilledChecklist *checklist = Filled(static_cast(data)); - assert(checklist->asyncState() == IdentLookup::Instance()); if (ident) { xstrncpy(checklist->rfc931, ident, USER_IDENT_SZ); @@ -156,9 +158,7 @@ IdentLookup::LookupDone(const char *ident, void *data) if (checklist->conn() != NULL && checklist->conn()->clientConnection != NULL && !checklist->conn()->clientConnection->rfc931[0]) xstrncpy(checklist->conn()->clientConnection->rfc931, checklist->rfc931, USER_IDENT_SZ); - checklist->asyncInProgress(false); - checklist->changeState(ACLChecklist::NullState::Instance()); - checklist->matchNonBlocking(); + checklist->resumeNonBlockingCheck(IdentLookup::Instance()); } #endif /* USE_IDENT */ diff --git a/src/ip/QosConfig.h b/src/ip/QosConfig.h index e17377764b..c382b1e3cf 100644 --- a/src/ip/QosConfig.h +++ b/src/ip/QosConfig.h @@ -1,6 +1,7 @@ #ifndef SQUID_QOSCONFIG_H #define SQUID_QOSCONFIG_H +#include "acl/forward.h" #include "hier_code.h" #include "ip/forward.h" @@ -16,7 +17,6 @@ #include #endif -class ACLList; class fde; // TODO: move to new ACL framework diff --git a/src/log/CustomLog.h b/src/log/CustomLog.h index 77d3af4d61..b41ca08595 100644 --- a/src/log/CustomLog.h +++ b/src/log/CustomLog.h @@ -29,9 +29,9 @@ * */ //#include "format/Format.h" +#include "acl/forward.h" #include "log/Formats.h" -class ACLList; class Logfile; namespace Format { diff --git a/src/ssl/ProxyCerts.h b/src/ssl/ProxyCerts.h index d58b307b7c..a12c8ccb2d 100644 --- a/src/ssl/ProxyCerts.h +++ b/src/ssl/ProxyCerts.h @@ -30,7 +30,7 @@ */ #if USE_SSL -class ACLList; +#include "acl/forward.h" class sslproxy_cert_sign { -- 2.39.5