]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Major ACL handling update, including the following changes:
authorAlex Rousskov <rousskov@measurement-factory.com>
Mon, 13 May 2013 22:48:23 +0000 (16:48 -0600)
committerAlex Rousskov <rousskov@measurement-factory.com>
Mon, 13 May 2013 22:48:23 +0000 (16:48 -0600)
* 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.

52 files changed:
src/AclRegs.cc
src/CachePeer.h
src/ClientDelayConfig.cc
src/ClientDelayConfig.h
src/DelayConfig.cc
src/DelayPool.h
src/HttpHeaderTools.h
src/Notes.cc
src/Notes.h
src/SquidConfig.h
src/acl/Acl.cc
src/acl/Acl.h
src/acl/AclNameList.h
src/acl/AclSizeLimit.h
src/acl/AllOf.cc [new file with mode: 0644]
src/acl/AllOf.h [new file with mode: 0644]
src/acl/AnyOf.cc [new file with mode: 0644]
src/acl/AnyOf.h [new file with mode: 0644]
src/acl/Asn.cc
src/acl/Checklist.cc
src/acl/Checklist.h
src/acl/DestinationDomain.cc
src/acl/DestinationIp.cc
src/acl/FilledChecklist.h
src/acl/Gadgets.cc
src/acl/Gadgets.h
src/acl/InnerNode.cc [new file with mode: 0644]
src/acl/InnerNode.h [new file with mode: 0644]
src/acl/Makefile.am
src/acl/SourceDomain.cc
src/acl/Tree.cc [new file with mode: 0644]
src/acl/Tree.h [new file with mode: 0644]
src/acl/forward.h [new file with mode: 0644]
src/adaptation/AccessRule.cc
src/adaptation/AccessRule.h
src/adaptation/Config.h
src/adaptation/icap/Config.h
src/auth/Acl.cc
src/auth/AclMaxUserIp.cc
src/auth/AclProxyAuth.cc
src/cache_cf.cc
src/cf.data.pre
src/cf_gen.cc
src/client_side_request.cc
src/client_side_request.h
src/defines.h
src/external_acl.cc
src/globals.h
src/ident/AclIdent.cc
src/ip/QosConfig.h
src/log/CustomLog.h
src/ssl/ProxyCerts.h

index fb112f18bae6638bbca82ce45753be2c92deb481..3b5cb0bd8674cdf8957168e8452af9318f12c491 100644 (file)
@@ -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<const char *> 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_;
index ec00d3031e1aed1864de77598022be754376618f..67aebfb26dcca1569736152cfd320d98d77239c6 100644 (file)
@@ -29,6 +29,7 @@
  *
  */
 
+#include "acl/forward.h"
 #include "enums.h"
 #include "icp_opcode.h"
 #include "ip/Address.h"
@@ -40,7 +41,6 @@
 #include <openssl/ssl.h>
 #endif
 
-class acl_access;
 class CachePeerDomainList;
 class NeighborTypeDomainList;
 class PeerDigest;
index d72e7a282d82d1e2bbbd9eeee12cf86b751e8bec..0d95212f786ec722576c39bf3035286303eeda83 100644 (file)
@@ -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()
index d48f93d60093c8eba6b35d57c6725c124f9a45c6..f3fa2d4eb0dcf1ee932fa162a647318f35366c7d 100644 (file)
@@ -2,8 +2,8 @@
 #define SQUID_CLIENTDELAYCONFIG_H
 
 #include "Array.h"
+#include "acl/forward.h"
 class StoreEntry;
-class acl_access;
 class ConfigParser;
 
 /// \ingroup DelayPoolsAPI
index 70dbebbb4f5382c89701af5b25c6208b240af42b..070c55d7afe880bfacf96d34c1a301fd162d795a 100644 (file)
@@ -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
index df5224c0f5bd37b953ac417f8c15eef9ece7e084..36e1fc04bb39c7bc70caf56e4de3206dae1ad235 100644 (file)
 
 #if USE_DELAY_POOLS
 #include "CompositePoolNode.h"
+#include "acl/forward.h"
 
 class StoreEntry;
 
 class CommonPool;
 
-class acl_access;
-
 /// \ingroup DelayPoolsAPI
 class DelayPool
 {
index dafa78734af8d1708ac61758f4bfd813e61dd371..5f097977d86044f764d8a6b851750b07d021b250 100644 (file)
@@ -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 <string>
 #endif
 
-class acl_access;
-class ACLList;
 class HeaderWithAcl;
 class HttpHeader;
 class HttpHeaderFieldInfo;
index 131cf255ac1b9552f938305cca5061a9fafe1e15..7cddf8b5207f4c5ed37ccf4fa0bba0096e9ef59b 100644 (file)
@@ -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, &noteValue->aclList);
+
+    String label(key);
+    label.append('=');
+    label.append(value);
+    aclParseAclList(parser, &noteValue->aclList, label.termedBuf());
 
     if (blacklisted) {
         for (int i = 0; blacklisted[i] != NULL; ++i) {
index 6d9fd3ca9e41faa19137a9fe87d437696fd8c119..a595ad03f01b17b058cb180b518aeb651bae94a1 100644 (file)
@@ -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
index b64ae893565d52bee56aeead0450b054e978ab4b..db3b02f25c880291a939ba9c78c6444775e780ee 100644 (file)
@@ -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;
index 2eeef57fab252947d184e738bf353b902f68aebb..b1e3fd8b9146378ed189bbdbfd694400796d4ce9 100644 (file)
@@ -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<ACL*>(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<acl_access *>(address);
-    cbdataFree(t);
-}
 
 ACL::Prototype::Prototype() : prototype (NULL), typeString (NULL) {}
 
index e7ae5af6f372641b0b49db1a4cc92e40bc205aa0..0b233267de9ffe55076907d9400f6f6a1ac88d77 100644 (file)
@@ -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<Prototype const*>::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
 {
index 5acbc7ba017129c00edf2b1cbebfcd9b460f302c..0b2825c0f5ecaa35886a1a26fd1ea0095ef5c790 100644 (file)
@@ -29,7 +29,7 @@
  *
  */
 
-#include "defines.h"
+#include "acl/forward.h"
 
 /// list of name-based ACLs. Currently a POD.
 class AclNameList
index ed6e4f0401483e8787028ad60b056cca64f38e94..be8a4bb40668f59bdce10d65a1cdebd67e567e80 100644 (file)
@@ -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 (file)
index 0000000..57e67a3
--- /dev/null
@@ -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<Acl::OrNode*>(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 (file)
index 0000000..7a7f4ce
--- /dev/null
@@ -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 (file)
index 0000000..8455675
--- /dev/null
@@ -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 (file)
index 0000000..e1536ee
--- /dev/null
@@ -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 */
index 43fe185973c94158cd8ff22f5846b7462819223e..f7d3ca447b1bf5a0ef829bb3d005c6300a07c497 100644 (file)
@@ -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<MatchType> * &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 *
index 9b98028c16fded3c0e7e7ea6637f90c01fcd61cb..a4cec177f89e2e36cdf47f9e503c8374105e5f45 100644 (file)
@@ -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 <robertc@squid-cache.org>
  */
 
 #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()
 {
index 8efdf8752059520176deb7944e7901ae5752d779..ee56f90ed72029fcab1c334dd84224e4f114e393 100644 (file)
@@ -31,7 +31,8 @@
 #ifndef SQUID_ACLCHECKLIST_H
 #define SQUID_ACLCHECKLIST_H
 
-#include "acl/Acl.h"
+#include "acl/InnerNode.h"
+#include <stack>
 
 /// 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 &currentAnswer() 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<Breadcrumb> matchPath;
 };
 
 #endif /* SQUID_ACLCHECKLIST_H */
index 82c1e3dea1134d1351ee2c9c6532a02d15021da6..88244faec7ec7fdd1af0303834e76e3c883b8153 100644 (file)
@@ -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<MatchType> * &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");
index 86825b74f4f1fe52fbaa19bbf87449e70821fa73..f5fb26c7a8d17dd3a027ecce39adc28c8d0ddfef 100644 (file)
@@ -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 *
index 362711bf9ffef786d55f19619233958e403f0b60..e5470c7f7fe57ea7a67c207a98b013ea725687cb 100644 (file)
@@ -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"
index f6fd6ba3cb547b5c3e559dd12d2409c612debc50..d43f89f58c3841f34f19839cdbc5b9eeb1e3bd18 100644 (file)
@@ -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)
index d59175cae28c3a2d1de213eeb301aff2a663fe92..12852a20b10b786859cf47c07bb6c418e31c588e 100644 (file)
@@ -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 <class Any>
+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 (file)
index 0000000..98d62dc
--- /dev/null
@@ -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 <algorithm>
+
+
+// "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 (file)
index 0000000..832ab73
--- /dev/null
@@ -0,0 +1,47 @@
+#ifndef SQUID_ACL_INNER_NODE_H
+#define SQUID_ACL_INNER_NODE_H
+
+#include "acl/Acl.h"
+#include <vector>
+
+namespace Acl {
+
+typedef std::vector<ACL*> 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<ACL*> nodes; ///< children nodes of this intermediate node
+};
+
+} // namespace Acl
+
+#endif /* SQUID_ACL_INNER_NODE_H */
index 3a1d122766049ac4c8fe37fc0d2d3bbe22b4b173..71d2b12fcb755d51c3fea3b49970f7ca915668e7 100644 (file)
@@ -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 \
index dced292ccf341db356927c46aa61100b0e563c06..a68ed988d82114abb2dbbd18c05f23edebd2a1e6 100644 (file)
@@ -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<MatchType> * &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 (file)
index 0000000..552948f
--- /dev/null
@@ -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 (file)
index 0000000..c244daf
--- /dev/null
@@ -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 <name, action, rule, new line> 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<allow_t> 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 (file)
index 0000000..4c5f39b
--- /dev/null
@@ -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 */
index 28cd2832b71d77d9cac6758cc725c990b2fc7256..8331656aa1581bcca3475cd2525c0c68c1683098 100644 (file)
@@ -20,7 +20,7 @@ Adaptation::AccessRule::~AccessRule()
 void
 Adaptation::AccessRule::parse(ConfigParser &parser)
 {
-    aclParseAccessLine(parser, &acl);
+    aclParseAccessLine("adaptation_access", parser, &acl);
 }
 
 void
index 09ad60f53fc603d0beb41d57eb3098141eb27705..9fae4ded79b1c96e2662a2158055acc35a5cbdb3 100644 (file)
@@ -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
index 65061a325c077d1e983a26e7b3682fa8bba09e87..478ce321c33d38b83297046ff1a41a26c928c5b6 100644 (file)
@@ -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;
index f0ae4feaaa80ae3c66f88156db9e6c750cc104b6..012efb7e43bf9d7084744cb2e26ef302e9c2a169 100644 (file)
 
 #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
index e388d684968f9432e13095a58e49743f5c514147..024db72269dfcad3629e3616c33b45352645c1b5 100644 (file)
@@ -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:
index e297ddad124257a51b745fc0aacd02bd246bb8d5..1b197c0e1c214670793ed777710d4af8fa091ed2 100644 (file)
@@ -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
     }
index 60d17ccba60a399023911fc99658670e1434cd55..2f2dbd30b9e054e8e949e5f695f2e91f140ef9fb 100644 (file)
@@ -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<ACLChecklist*>(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 *
index 9485c8eca2500bfe3db8711b7560f807d19201ee..ccb64d54d4b1a67b7293205ea8f453900af56100 100644 (file)
@@ -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 <acl>\" to \"ssl_bump client-first <acl>\" 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 <acl>\" to \"ssl_bump none <acl>\". 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);
 }
 
index 7ad9637f5ef1746e685d2e217d12319920961f22..21a1aac86dee17fb4f379417ef1277293f4175dd 100644 (file)
@@ -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
index b19b97452c202e17d19ca322ff92b6637240b4d5..7d9e5591d183c73788aea98dc078ec37256e948f 100644 (file)
@@ -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 <<
index 97f858d39d27073e560a3207cddf12df32378818..237c0de8fc48a829ec580ede6a24078f9fed75f6 100644 (file)
@@ -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 = "<null>";
index eaa5f9211e4b045ed4c2d10bb1d9cf5de3407465..53d5ac7e7371bb912db4d4a4ec080a1c5411a0c9 100644 (file)
@@ -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;
index 1828701ddd7d425705ce1ee21400f24d5ab64b58..890c5c468008162d6c6a8e8c0989139d269f8c7d 100644 (file)
@@ -41,7 +41,6 @@
 #define BUFSIZ  4096            /* make unreasonable guess */
 #endif
 
-#define ACL_NAME_SZ 32
 #define BROWSERNAMELEN 128
 
 #define ACL_SUNDAY     0x01
index c6a5b6f376fe3d2a122ed1385f88362fc46994fe..2b3b73d6adc9f30888365dbd82b65b60dec64930 100644 (file)
@@ -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<ACLExternal *> (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<ACLChecklist*>(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
index 211dcf84ce28f0949ec633a0f5b57e0ad41d1630..2967d45eaf01dc748698d2e11ff60253c5aea113 100644 (file)
@@ -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;   /* "-" */
index 140eace51b8ffe015e0c194b2ae1fb0f7cf63653..7ab36c5821c014c81a9485d7ced1b296f602217c 100644 (file)
@@ -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<ACLChecklist*>(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 */
index e17377764bf586f946da349d38f06c71e6eb610b..c382b1e3cf85dfa1a0c0fd64f28162679b95c583 100644 (file)
@@ -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 <limits>
 #endif
 
-class ACLList;
 class fde;
 
 // TODO: move to new ACL framework
index 77d3af4d61419c0222dadda185088d0a8591d7d9..b41ca0859552e79346b83d7a5e51126e3623ccda 100644 (file)
@@ -29,9 +29,9 @@
  *
  */
 //#include "format/Format.h"
+#include "acl/forward.h"
 #include "log/Formats.h"
 
-class ACLList;
 class Logfile;
 namespace Format
 {
index d58b307b7c1de528239d27c7ae7443c33b242661..a12c8ccb2decb50ac736ddafcb7812b898b209dd 100644 (file)
@@ -30,7 +30,7 @@
  */
 
 #if USE_SSL
-class ACLList;
+#include "acl/forward.h"
 
 class sslproxy_cert_sign
 {