# Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: rousskov@measurement-factory.com-20130513233223-\ # 6d58cyjzs2k66k0g # target_branch: http://www.squid-cache.org/bzr/squid3/trunk # testament_sha1: b16b61dd04ec7cfbda49393b1c793ecc3d4e5aa1 # timestamp: 2013-05-13 17:34:11 -0600 # base_revision_id: squid3@treenet.co.nz-20130513163633-\ # rsh0w1qy0xqojqt9 # # Begin patch === modified file 'src/AclRegs.cc' --- src/AclRegs.cc 2013-01-27 17:35:07 +0000 +++ src/AclRegs.cc 2013-05-13 22:48:23 +0000 @@ -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 @@ ACL::Prototype ACLTag::RegistryProtoype(&ACLTag::RegistryEntry_, "tag"); ACLStrategised ACLTag::RegistryEntry_(new ACLStringData, ACLTagStrategy::Instance(), "tag"); + +ACL::Prototype Acl::AnyOf::RegistryProtoype(&Acl::AnyOf::RegistryEntry_, "any-of"); +Acl::AnyOf Acl::AnyOf::RegistryEntry_; + +ACL::Prototype Acl::AllOf::RegistryProtoype(&Acl::AllOf::RegistryEntry_, "all-of"); +Acl::AllOf Acl::AllOf::RegistryEntry_; === modified file 'src/CachePeer.h' --- src/CachePeer.h 2013-01-25 16:01:16 +0000 +++ src/CachePeer.h 2013-05-13 22:48:23 +0000 @@ -29,6 +29,7 @@ * */ +#include "acl/forward.h" #include "enums.h" #include "icp_opcode.h" #include "ip/Address.h" @@ -40,7 +41,6 @@ #include #endif -class acl_access; class CachePeerDomainList; class NeighborTypeDomainList; class PeerDigest; === modified file 'src/ClientDelayConfig.cc' --- src/ClientDelayConfig.cc 2012-08-14 11:53:07 +0000 +++ src/ClientDelayConfig.cc 2013-05-13 22:48:23 +0000 @@ -83,7 +83,7 @@ } --pool; - aclParseAccessLine(parser, &pools[pool].access); + aclParseAccessLine("client_delay_access", parser, &pools[pool].access); } void ClientDelayConfig::clean() === modified file 'src/ClientDelayConfig.h' --- src/ClientDelayConfig.h 2013-05-04 11:50:26 +0000 +++ src/ClientDelayConfig.h 2013-05-13 23:32:23 +0000 @@ -1,10 +1,10 @@ #ifndef SQUID_CLIENTDELAYCONFIG_H #define SQUID_CLIENTDELAYCONFIG_H +#include "acl/forward.h" #include "base/Vector.h" class StoreEntry; -class acl_access; class ConfigParser; /// \ingroup DelayPoolsAPI === modified file 'src/DelayConfig.cc' --- src/DelayConfig.cc 2012-09-01 14:38:36 +0000 +++ src/DelayConfig.cc 2013-05-13 22:48:23 +0000 @@ -113,7 +113,7 @@ } --pool; - aclParseAccessLine(parser, &DelayPools::delay_data[pool].access); + aclParseAccessLine("delay_access", parser, &DelayPools::delay_data[pool].access); } void === modified file 'src/DelayPool.h' --- src/DelayPool.h 2012-09-01 14:38:36 +0000 +++ src/DelayPool.h 2013-05-13 22:48:23 +0000 @@ -39,13 +39,12 @@ #if USE_DELAY_POOLS #include "CompositePoolNode.h" +#include "acl/forward.h" class StoreEntry; class CommonPool; -class acl_access; - /// \ingroup DelayPoolsAPI class DelayPool { === modified file 'src/HttpHeaderTools.h' --- src/HttpHeaderTools.h 2013-02-17 09:31:18 +0000 +++ src/HttpHeaderTools.h 2013-05-13 22:48:23 +0000 @@ -1,6 +1,7 @@ #ifndef SQUID_HTTPHEADERTOOLS_H #define SQUID_HTTPHEADERTOOLS_H +#include "acl/forward.h" #include "format/Format.h" #include "HttpHeader.h" #include "typedefs.h" @@ -15,8 +16,6 @@ #include #endif -class acl_access; -class ACLList; class HeaderWithAcl; class HttpHeader; class HttpHeaderFieldInfo; === modified file 'src/Notes.cc' --- src/Notes.cc 2013-04-30 00:13:26 +0000 +++ src/Notes.cc 2013-05-13 23:32:23 +0000 @@ -96,7 +96,11 @@ ConfigParser::ParseQuotedString(&value); Note::Pointer note = add(key); Note::Value::Pointer noteValue = note->addValue(value); - aclParseAclList(parser, ¬eValue->aclList); + + String label(key); + label.append('='); + label.append(value); + aclParseAclList(parser, ¬eValue->aclList, label.termedBuf()); if (blacklisted) { for (int i = 0; blacklisted[i] != NULL; ++i) { === modified file 'src/Notes.h' --- src/Notes.h 2013-05-04 11:50:26 +0000 +++ src/Notes.h 2013-05-13 23:32:23 +0000 @@ -1,6 +1,7 @@ #ifndef SQUID_NOTES_H #define SQUID_NOTES_H +#include "acl/forward.h" #include "base/Vector.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 === modified file 'src/SquidConfig.h' --- src/SquidConfig.h 2013-05-13 03:57:03 +0000 +++ src/SquidConfig.h 2013-05-13 23:32:23 +0000 @@ -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_adapt; #endif -class acl_access; -class AclSizeLimit; -class AclDenyInfoList; namespace Mgr { class ActionPasswordList; === modified file 'src/acl/Acl.cc' --- src/acl/Acl.cc 2013-05-05 08:38:06 +0000 +++ src/acl/Acl.cc 2013-05-13 23:32:23 +0000 @@ -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}; @@ -138,7 +138,8 @@ ACL::ACL() : cfgline(NULL), - next(NULL) + next(NULL), + registered(false) { *name = 0; } @@ -148,6 +149,46 @@ return true; } +bool +ACL::matches(ACLChecklist *checklist) const +{ + PROF_start(ACL_matches); + debugs(28, 5, "checking " << name); + + // XXX: AclMatchedName does not contain a matched ACL name when the acl + // does not match. It contains the last (usually leaf) ACL name checked + // (or is NULL if no ACLs were checked). + AclMatchedName = name; + + int result = 0; + if (!checklist->hasRequest() && requiresRequest()) { + debugs(28, DBG_IMPORTANT, "WARNING: " << name << " ACL is used in " << + "context without an HTTP request. Assuming mismatch."); + } else if (!checklist->hasReply() && requiresReply()) { + debugs(28, DBG_IMPORTANT, "WARNING: " << name << " ACL is used in " << + "context without an HTTP response. Assuming mismatch."); + } else { + // have to cast because old match() API is missing const + result = const_cast(this)->match(checklist); + } + + const char *extra = checklist->asyncInProgress() ? " async" : ""; + debugs(28, 3, "checked: " << name << " = " << result << extra); + PROF_stop(ACL_matches); + return result == 1; // true for match; false for everything else +} + +void +ACL::context(const char *aName, const char *aCfgLine) +{ + name[0] = '\0'; + if (aName) + xstrncpy(name, aName, ACL_NAME_SZ-1); + safe_free(cfgline); + if (aCfgLine) + cfgline = xstrdup(aCfgLine); +} + void ACL::ParseAclLine(ConfigParser &parser, ACL ** head) { @@ -216,8 +257,7 @@ 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 @@ } /* append */ + assert(head && *head == Config.aclList); + A->registered = true; + while (*head) head = &(*head)->next; @@ -271,18 +314,6 @@ 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 @@ 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 @@ { debugs(28, 3, "ACL::~ACL: '" << cfgline << "'"); safe_free(cfgline); -} - -/* to be split into separate files in the future */ - -CBDATA_CLASS_INIT(acl_access); - -void * -acl_access::operator new (size_t) -{ - CBDATA_INIT_TYPE(acl_access); - acl_access *result = cbdataAlloc(acl_access); - return result; -} - -void -acl_access::operator delete (void *address) -{ - acl_access *t = static_cast(address); - cbdataFree(t); -} + AclMatchedName = NULL; // in case it was pointing to our name +} + ACL::Prototype::Prototype() : prototype (NULL), typeString (NULL) {} === modified file 'src/acl/Acl.h' --- src/acl/Acl.h 2013-05-05 08:38:06 +0000 +++ src/acl/Acl.h 2013-05-13 23:32:23 +0000 @@ -33,6 +33,7 @@ #ifndef SQUID_ACL_H #define SQUID_ACL_H +#include "acl/forward.h" #include "base/Vector.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 @@ 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 @@ 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 @@ 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 @@ typedef Vector::const_iterator const_iterator; void registerMe(); }; + +private: + /// Matches the actual data in checklist against this ACL. + virtual int match(ACLChecklist *checklist) = 0; // XXX: missing const + + /// whether our (i.e. shallow) match() requires checklist to have a request + virtual bool requiresRequest() const; + /// whether our (i.e. shallow) match() requires checklist to have a reply + virtual bool requiresReply() const; }; /// \ingroup ACLAPI @@ -211,39 +232,6 @@ } /// \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 { === modified file 'src/acl/AclNameList.h' --- src/acl/AclNameList.h 2012-09-21 13:27:44 +0000 +++ src/acl/AclNameList.h 2013-05-13 22:48:23 +0000 @@ -29,7 +29,7 @@ * */ -#include "defines.h" +#include "acl/forward.h" /// list of name-based ACLs. Currently a POD. class AclNameList === modified file 'src/acl/AclSizeLimit.h' --- src/acl/AclSizeLimit.h 2012-10-04 00:23:44 +0000 +++ src/acl/AclSizeLimit.h 2013-05-13 22:48:23 +0000 @@ -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 === added file 'src/acl/AllOf.cc' --- src/acl/AllOf.cc 1970-01-01 00:00:00 +0000 +++ src/acl/AllOf.cc 2013-05-13 22:48:23 +0000 @@ -0,0 +1,83 @@ +#include "squid.h" +#include "acl/AllOf.h" +#include "acl/Checklist.h" +#include "acl/BoolOps.h" +#include "globals.h" +#include "MemBuf.h" + + +char const * +Acl::AllOf::typeString() const +{ + return "all-of"; +} + +ACL * +Acl::AllOf::clone() const +{ + return new AllOf; +} + +wordlist* +Acl::AllOf::dump() const +{ + return empty() ? NULL : nodes.front()->dump(); +} + +int +Acl::AllOf::doMatch(ACLChecklist *checklist, Nodes::const_iterator start) const +{ + assert(start == nodes.begin()); // we only have one node + + // avoid dereferencing invalid start + if (empty()) + return 1; // not 0 because in math empty product equals identity + + if (checklist->matchChild(this, start, *start)) + return 1; // match + + return checklist->keepMatching() ? 0 : -1; +} + +// called once per "acl name all-of name1 name2 ...." line +void +Acl::AllOf::parse() +{ + Acl::InnerNode *whole = NULL; + ACL *oldNode = empty() ? NULL : nodes.front(); + + // optimization: this logic reduces subtree hight (number of tree levels) + if (Acl::OrNode *oldWhole = dynamic_cast(oldNode)) { + // this acl saw multiple lines before; add another one to the old node + whole = oldWhole; + } else if (oldNode) { + // this acl saw a single line before; create a new OR inner node + + MemBuf wholeCtx; + wholeCtx.init(); + wholeCtx.Printf("(%s lines)", name); + wholeCtx.terminate(); + + Acl::OrNode *newWhole = new Acl::OrNode; + newWhole->context(wholeCtx.content(), oldNode->cfgline); + newWhole->add(oldNode); // old (i.e. first) line + nodes.front() = whole = newWhole; + } else { + // this is the first line for this acl; just use it as is + whole = this; + } + + assert(whole); + const int lineId = whole->childrenCount() + 1; + + MemBuf lineCtx; + lineCtx.init(); + lineCtx.Printf("(%s line #%d)", name, lineId); + lineCtx.terminate(); + + Acl::AndNode *line = new AndNode; + line->context(lineCtx.content(), config_input_line); + line->lineParse(); + + whole->add(line); +} === added file 'src/acl/AllOf.h' --- src/acl/AllOf.h 1970-01-01 00:00:00 +0000 +++ src/acl/AllOf.h 2013-05-13 22:48:23 +0000 @@ -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 */ === added file 'src/acl/AnyOf.cc' --- src/acl/AnyOf.cc 1970-01-01 00:00:00 +0000 +++ src/acl/AnyOf.cc 2013-05-13 22:48:23 +0000 @@ -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(); +} === added file 'src/acl/AnyOf.h' --- src/acl/AnyOf.h 1970-01-01 00:00:00 +0000 +++ src/acl/AnyOf.h 2013-05-13 22:48:23 +0000 @@ -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 */ === modified file 'src/acl/Asn.cc' --- src/acl/Asn.cc 2013-03-18 04:55:51 +0000 +++ src/acl/Asn.cc 2013-05-13 22:48:23 +0000 @@ -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 @@ /* 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 * === added file 'src/acl/BoolOps.cc' --- src/acl/BoolOps.cc 1970-01-01 00:00:00 +0000 +++ src/acl/BoolOps.cc 2013-05-13 22:51:02 +0000 @@ -0,0 +1,139 @@ +#include "squid.h" +#include "acl/BoolOps.h" +#include "acl/Checklist.h" +#include "Debug.h" +#include "wordlist.h" + + +/* Acl::NotNode */ + +Acl::NotNode::NotNode(ACL *acl) +{ + assert(acl); + name[0] = '!'; + strncat(&name[1], acl->name, sizeof(name)-1-1); + add(acl); +} + +void +Acl::NotNode::parse() +{ + // Not implemented: by the time an upper level parser discovers + // an '!' operator, there is nothing left for us to parse. + assert(false); +} + +int +Acl::NotNode::doMatch(ACLChecklist *checklist, Nodes::const_iterator start) const +{ + assert(start == nodes.begin()); // we only have one node + + if (checklist->matchChild(this, start, *start)) + return 0; // converting match into mismatch + + if (!checklist->keepMatching()) + return -1; // suspend on async calls and stop on failures + + return 1; // converting mismatch into match +} + +char const * +Acl::NotNode::typeString() const +{ + return "!"; +} + +ACL * +Acl::NotNode::clone() const +{ + // Not implemented: we are not a named ACL type in squid.conf so nobody + // should try to create a NotNode instance by ACL type name (which is + // what clone() API is for -- it does not really clone anything). + assert(false); + return NULL; +} + +wordlist* +Acl::NotNode::dump() const +{ + wordlist *text = NULL; + wordlistAdd(&text, name); + return text; +} + + +/* Acl::AndNode */ + +char const * +Acl::AndNode::typeString() const +{ + return "and"; +} + +ACL * +Acl::AndNode::clone() const +{ + return new AndNode; +} + +int +Acl::AndNode::doMatch(ACLChecklist *checklist, Nodes::const_iterator start) const +{ + // find the first node that does not match + for (Nodes::const_iterator i = start; i != nodes.end(); ++i) { + if (!checklist->matchChild(this, i, *i)) + return checklist->keepMatching() ? 0 : -1; + } + + // one and not zero on empty because in math empty product equals identity + return 1; // no mismatches found (i.e., all kids matched) +} + +void +Acl::AndNode::parse() +{ + // Not implemented: AndNode cannot be configured directly. See Acl::AllOf. + assert(false); +} + + +/* Acl::OrNode */ + +char const * +Acl::OrNode::typeString() const +{ + return "any-of"; +} + +ACL * +Acl::OrNode::clone() const +{ + return new OrNode; +} + +int +Acl::OrNode::doMatch(ACLChecklist *checklist, Nodes::const_iterator start) const +{ + lastMatch_ = nodes.end(); + + // find the first node that matches, but stop if things go wrong + for (Nodes::const_iterator i = start; i != nodes.end(); ++i) { + if (checklist->matchChild(this, i, *i)) { + lastMatch_ = i; + return 1; + } + + if (!checklist->keepMatching()) + return -1; // suspend on async calls and stop on failures + } + + // zero and not one on empty because in math empty sum equals zero + return 0; // all nodes mismatched +} + +void +Acl::OrNode::parse() +{ + // Not implemented: OrNode cannot be configured directly. See Acl::AnyOf. + assert(false); +} === added file 'src/acl/BoolOps.h' --- src/acl/BoolOps.h 1970-01-01 00:00:00 +0000 +++ src/acl/BoolOps.h 2013-05-13 22:51:02 +0000 @@ -0,0 +1,75 @@ +#ifndef SQUID_ACL_LOGIC_H +#define SQUID_ACL_LOGIC_H + +#include "acl/InnerNode.h" + +/* ACLs defined here are used internally to construct an ACL expression tree. + * They cannot be specified directly in squid.conf because squid.conf ACLs are + * more complex than (and are implemented using) these operator-like classes.*/ + +namespace Acl { + +/// Implements the "not" or "!" operator. +class NotNode: public InnerNode +{ +public: + MEMPROXY_CLASS(NotNode); + + explicit NotNode(ACL *acl); + +private: + /* ACL API */ + virtual char const *typeString() const; + virtual ACL *clone() const; + virtual void parse(); + virtual wordlist *dump() const; + + /* Acl::InnerNode API */ + virtual int doMatch(ACLChecklist *checklist, Nodes::const_iterator start) const; +}; +MEMPROXY_CLASS_INLINE(Acl::NotNode); + + +/// An inner ACL expression tree node representing a boolean conjuction (AND) +/// operator applied to a list of child tree nodes. +/// For example, conditions expressed on a single http_access line are ORed. +class AndNode: public InnerNode +{ +public: + MEMPROXY_CLASS(AndNode); + + /* ACL API */ + virtual char const *typeString() const; + virtual ACL *clone() const; + virtual void parse(); + +private: + virtual int doMatch(ACLChecklist *checklist, Nodes::const_iterator start) const; +}; +MEMPROXY_CLASS_INLINE(Acl::AndNode); + +/// An inner ACL expression tree node representing a boolean disjuction (OR) +/// operator applied to a list of child tree nodes. +/// For example, conditions expressed by multiple http_access lines are ORed. +class OrNode: public InnerNode +{ +public: + MEMPROXY_CLASS(OrNode); + + /* ACL API */ + virtual char const *typeString() const; + virtual ACL *clone() const; + virtual void parse(); + +protected: + mutable Nodes::const_iterator lastMatch_; + +private: + virtual int doMatch(ACLChecklist *checklist, Nodes::const_iterator start) const; +}; +MEMPROXY_CLASS_INLINE(Acl::OrNode); + + +} // namespace Acl + +#endif /* SQUID_ACL_LOGIC_H */ === modified file 'src/acl/Checklist.cc' --- src/acl/Checklist.cc 2012-11-15 07:35:32 +0000 +++ src/acl/Checklist.cc 2013-05-13 22:48:23 +0000 @@ -1,66 +1,24 @@ /* * DEBUG: section 28 Access Control - * AUTHOR: Duane Wessels - * - * SQUID Web Proxy Cache http://www.squid-cache.org/ - * ---------------------------------------------------------- - * - * Squid is the result of efforts by numerous individuals from - * the Internet community; see the CONTRIBUTORS file for full - * details. Many organizations have provided support for Squid's - * development; see the SPONSORS file for full details. Squid is - * Copyrighted (C) 2001 by the Regents of the University of - * California; see the COPYRIGHT file for full details. Squid - * incorporates software developed and/or copyrighted by other - * sources; see the CREDITS file for full details. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. - * - * Copyright (c) 2003, Robert Collins */ #include "squid.h" #include "acl/Checklist.h" +#include "acl/Tree.h" #include "Debug.h" #include "profiler/Profiler.h" -void -ACLChecklist::matchNonBlocking() +/// common parts of nonBlockingCheck() and resumeNonBlockingCheck() +bool +ACLChecklist::prepNonBlocking() { - if (checking()) - return; + assert(accessList); if (callerGone()) { checkCallback(ACCESS_DUNNO); // the answer does not really matter - return; - } - - /** The ACL List should NEVER be NULL when calling this method. - * Always caller should check for NULL and handle appropriate to its needs first. - * We cannot select a sensible default for all callers here. */ - if (accessList == NULL) { - debugs(28, DBG_CRITICAL, "SECURITY ERROR: ACL " << this << " checked with nothing to match against!!"); - checkCallback(ACCESS_DUNNO); - return; - } - - allow_t lastSeenKeyword = ACCESS_DUNNO; - /* NOTE: This holds a cbdata reference to the current access_list - * entry, not the whole list. - */ - while (accessList != NULL) { + return false; + } + /** \par * If the _acl_access is no longer valid (i.e. its been * freed because of a reconfigure), then bail with ACCESS_DUNNO. @@ -70,73 +28,36 @@ cbdataReferenceDone(accessList); debugs(28, 4, "ACLChecklist::check: " << this << " accessList is invalid"); checkCallback(ACCESS_DUNNO); - return; - } - - checking (true); - checkAccessList(); - checking (false); - - if (asyncInProgress()) { - return; - } - - if (finished()) { + return false; + } + + // 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; + /** \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); + return false; +} + +void +ACLChecklist::completeNonBlocking() +{ + assert(!asyncInProgress()); + + if (!finished()) + calcImplicitAnswer(); + + cbdataReferenceDone(accessList); checkCallback(currentAnswer()); } -bool -ACLChecklist::asyncNeeded() const -{ - return state_ != NullState::Instance(); -} - -bool -ACLChecklist::asyncInProgress() const -{ - return async_; -} - -void -ACLChecklist::asyncInProgress(bool const newAsync) -{ - assert (!finished() && !(asyncInProgress() && newAsync)); - async_ = newAsync; - debugs(28, 3, "ACLChecklist::asyncInProgress: " << this << - " async set to " << async_); -} - -bool -ACLChecklist::finished() const -{ - return finished_; -} - void ACLChecklist::markFinished(const allow_t &finalAnswer, const char *reason) { @@ -151,25 +72,77 @@ 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"); - - // If we are not finished() here, the caller must distinguish between - // slow async calls and pure rule mismatches using asyncInProgress(). + 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(); + } + + 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ /// 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; @@ -458,18 +372,6 @@ } bool -ACLChecklist::checking() const -{ - return checking_; -} - -void -ACLChecklist::checking (bool const newValue) -{ - checking_ = newValue; -} - -bool ACLChecklist::callerGone() { return !cbdataReferenceValid(callback_data); === modified file 'src/acl/Checklist.h' --- src/acl/Checklist.h 2012-09-01 14:38:36 +0000 +++ src/acl/Checklist.h 2013-05-13 22:48:23 +0000 @@ -31,7 +31,8 @@ #ifndef SQUID_ACLCHECKLIST_H #define SQUID_ACLCHECKLIST_H -#include "acl/Acl.h" +#include "acl/InnerNode.h" +#include /// ACL checklist callback typedef void ACLCB(allow_t, void *); @@ -67,9 +68,6 @@ public: virtual void checkForAsync(ACLChecklist *) const = 0; virtual ~AsyncState() {} - - protected: - void changeState (ACLChecklist *, AsyncState *) const; }; class NullState : public AsyncState @@ -153,25 +151,29 @@ * * If there are no ACLs to check at all, the result becomes ACCESS_ALLOWED. */ - allow_t const & fastCheck(const ACLList * list); - - // 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); + 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 we should continue to match tree nodes or stop/pause. + bool keepMatching() const { return !finished() && !asyncInProgress(); } /// whether markFinished() was called - bool finished() const; + bool finished() const { return finished_; } + /// async call has been started and has not finished (or failed) yet + bool asyncInProgress() const { return asyncStage_ != asyncNone; } /// called when no more ACLs should be checked; sets the final answer and /// prints a debugging message explaining the reason for that answer void markFinished(const allow_t &newAnswer, const char *reason); const allow_t ¤tAnswer() const { return allow_; } - void changeState(AsyncState *); - AsyncState *asyncState() const; - // XXX: ACLs that need request or reply have to use ACLFilledChecklist and // should do their own checks so that we do not have to povide these two // for ACL::checklistMatches to use @@ -182,46 +184,58 @@ /// 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_; + + enum AsyncStage { asyncNone, asyncStarting, asyncRunning, asyncFailed }; + AsyncStage asyncStage_; AsyncState *state_; - - bool checking_; - bool checking() const; - void checking (bool const); + Breadcrumb matchLoc_; ///< location of the node running matches() now + Breadcrumb asyncLoc_; ///< currentNode_ that called goAsync() bool callerGone(); + + /// suspended (due to an async lookup) matches() in the ACL tree + std::stack matchPath; }; #endif /* SQUID_ACLCHECKLIST_H */ === modified file 'src/acl/DestinationDomain.cc' --- src/acl/DestinationDomain.cc 2013-01-27 17:35:07 +0000 +++ src/acl/DestinationDomain.cc 2013-05-13 22:48:23 +0000 @@ -53,7 +53,6 @@ DestinationDomainLookup::checkForAsync(ACLChecklist *cl) const { ACLFilledChecklist *checklist = Filled(cl); - checklist->asyncInProgress(true); fqdncache_nbgethostbyaddr(checklist->dst_addr, LookupDone, checklist); } @@ -61,13 +60,9 @@ 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 @@ } 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"); === modified file 'src/acl/DestinationIp.cc' --- src/acl/DestinationIp.cc 2013-05-13 03:57:03 +0000 +++ src/acl/DestinationIp.cc 2013-05-13 23:32:23 +0000 @@ -86,11 +86,12 @@ } 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 @@ DestinationIPLookup::checkForAsync(ACLChecklist *cl)const { ACLFilledChecklist *checklist = Filled(cl); - checklist->asyncInProgress(true); ipcache_nbgethostbyname(checklist->request->GetHost(), LookupDone, checklist); } @@ -113,12 +113,9 @@ 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 * === modified file 'src/acl/FilledChecklist.h' --- src/acl/FilledChecklist.h 2012-11-13 18:19:17 +0000 +++ src/acl/FilledChecklist.h 2013-05-13 22:48:23 +0000 @@ -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" === modified file 'src/acl/Gadgets.cc' --- src/acl/Gadgets.cc 2013-05-11 20:59:44 +0000 +++ src/acl/Gadgets.cc 2013-05-13 23:32:23 +0000 @@ -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 @@ 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 @@ } 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 = parser.strtokFile())) { - 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 @@ } 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) === modified file 'src/acl/Gadgets.h' --- src/acl/Gadgets.h 2013-01-10 01:00:54 +0000 +++ src/acl/Gadgets.h 2013-05-13 22:48:23 +0000 @@ -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 aclDestroyAcls(ACL **); /// \ingroup ACLAPI void aclDestroyAclList(ACLList **); -/// \ingroup ACLAPI -void aclParseAccessLine(ConfigParser &parser, acl_access **); -/// \ingroup ACLAPI -void aclParseAclList(ConfigParser &parser, ACLList **); +/// Parses a single line of a "action followed by acls" directive (e.g., http_access). +/// \ingroup ACLAPI +void aclParseAccessLine(const char *directive, ConfigParser &parser, Acl::Tree **); +/// Parses a single line of a "some context followed by acls" directive (e.g., note n v). +/// The label parameter identifies the context (for debugging). +/// \ingroup ACLAPI +void aclParseAclList(ConfigParser &parser, Acl::Tree **, const char *label); +/// Template to convert various context lables to strings. \ingroup ACLAPI +template +inline +void aclParseAclList(ConfigParser &parser, Acl::Tree **tree, const Any any) { + std::ostringstream buf; + buf << any; + aclParseAclList(parser, tree, buf.str().c_str()); +} + /// \ingroup ACLAPI int aclIsProxyAuth(const char *name); /// \ingroup ACLAPI === added file 'src/acl/InnerNode.cc' --- src/acl/InnerNode.cc 1970-01-01 00:00:00 +0000 +++ src/acl/InnerNode.cc 2013-05-13 22:48:23 +0000 @@ -0,0 +1,110 @@ +#include "squid.h" +#include "acl/Acl.h" +#include "acl/BoolOps.h" +#include "acl/Checklist.h" +#include "acl/Gadgets.h" +#include "acl/InnerNode.h" +#include "cache_cf.h" +#include "ConfigParser.h" +#include "Debug.h" +#include "globals.h" +#include "wordlist.h" +#include + + +// "delete acl" class to use with std::for_each() in InnerNode::~InnerNode() +class AclDeleter { +public: + void operator()(ACL* acl) { + // Do not delete explicit ACLs; they are maintained by Config.aclList. + if (acl && !acl->registered) + delete acl; + } +}; + + +Acl::InnerNode::~InnerNode() +{ + std::for_each(nodes.begin(), nodes.end(), AclDeleter()); +} + +void +Acl::InnerNode::prepareForUse() +{ + std::for_each(nodes.begin(), nodes.end(), std::mem_fun(&ACL::prepareForUse)); +} + +bool +Acl::InnerNode::empty() const +{ + return nodes.empty(); +} + +void +Acl::InnerNode::add(ACL *node) +{ + assert(node != NULL); + nodes.push_back(node); +} + +// one call parses one "acl name acltype name1 name2 ..." line +// kids use this method to handle [multiple] parse() calls correctly +void +Acl::InnerNode::lineParse() +{ + // XXX: not precise, may change when looping or parsing multiple lines + if (!cfgline) + cfgline = xstrdup(config_input_line); + + // expect a list of ACL names, each possibly preceeded by '!' for negation + + while (const char *t = ConfigParser::strtokFile()) { + const bool negated = (*t == '!'); + if (negated) + ++t; + + debugs(28, 3, "looking for ACL " << t); + ACL *a = ACL::FindByName(t); + + if (a == NULL) { + debugs(28, DBG_CRITICAL, "ACL not found: " << t); + self_destruct(); + return; + } + + // append(negated ? new NotNode(a) : a); + if (negated) + add(new NotNode(a)); + else + add(a); + } + + return; +} + +wordlist* +Acl::InnerNode::dump() const +{ + wordlist *values = NULL; + for (Nodes::const_iterator i = nodes.begin(); i != nodes.end(); ++i) + wordlistAdd(&values, (*i)->name); + return values; +} + +int +Acl::InnerNode::match(ACLChecklist *checklist) +{ + return doMatch(checklist, nodes.begin()); +} + +bool +Acl::InnerNode::resumeMatchingAt(ACLChecklist *checklist, Nodes::const_iterator pos) const +{ + debugs(28, 5, "checking " << name << " at " << (pos-nodes.begin())); + const int result = doMatch(checklist, pos); + const char *extra = checklist->asyncInProgress() ? " async" : ""; + debugs(28, 3, "checked: " << name << " = " << result << extra); + + // merges async and failures (-1) into "not matched" + return result == 1; +} === added file 'src/acl/InnerNode.h' --- src/acl/InnerNode.h 1970-01-01 00:00:00 +0000 +++ src/acl/InnerNode.h 2013-05-13 22:48:23 +0000 @@ -0,0 +1,47 @@ +#ifndef SQUID_ACL_INNER_NODE_H +#define SQUID_ACL_INNER_NODE_H + +#include "acl/Acl.h" +#include + +namespace Acl { + +typedef std::vector Nodes; ///< a collection of nodes + +/// An intermediate ACL tree node. Manages a collection of child tree nodes. +class InnerNode: public ACL +{ +public: + virtual ~InnerNode(); + + /// Resumes matching (suspended by an async call) at the given position. + bool resumeMatchingAt(ACLChecklist *checklist, Acl::Nodes::const_iterator pos) const; + + /// the number of children nodes + Nodes::size_type childrenCount() const { return nodes.size(); } + + /* ACL API */ + virtual void prepareForUse(); + virtual bool empty() const; + virtual wordlist *dump() const; + + /// parses one "acl name type acl1 acl2..." line, appending to nodes + void lineParse(); + + /// appends the node to the collection and takes control over it + void add(ACL *node); + +protected: + /// checks whether the nodes match, starting with the given one + /// kids determine what a match means for their type of intermediate nodes + virtual int doMatch(ACLChecklist *checklist, Nodes::const_iterator start) const = 0; + + /* ACL API */ + virtual int match(ACLChecklist *checklist); + + std::vector nodes; ///< children nodes of this intermediate node +}; + +} // namespace Acl + +#endif /* SQUID_ACL_INNER_NODE_H */ === modified file 'src/acl/Makefile.am' --- src/acl/Makefile.am 2012-11-13 18:19:17 +0000 +++ src/acl/Makefile.am 2013-05-13 22:48:23 +0000 @@ -8,7 +8,14 @@ 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 @@ TimeData.h \ Asn.cc \ Asn.h \ + AllOf.cc \ + AllOf.h \ + AnyOf.cc \ + AnyOf.h \ Browser.cc \ Browser.h \ DestinationAsn.h \ === modified file 'src/acl/SourceDomain.cc' --- src/acl/SourceDomain.cc 2013-01-27 17:35:07 +0000 +++ src/acl/SourceDomain.cc 2013-05-13 22:48:23 +0000 @@ -51,7 +51,6 @@ void SourceDomainLookup::checkForAsync(ACLChecklist *checklist) const { - checklist->asyncInProgress(true); fqdncache_nbgethostbyaddr(Filled(checklist)->src_addr, LookupDone, checklist); } @@ -59,13 +58,9 @@ 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 @@ } 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"); === added file 'src/acl/Tree.cc' --- src/acl/Tree.cc 1970-01-01 00:00:00 +0000 +++ src/acl/Tree.cc 2013-05-13 22:48:23 +0000 @@ -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; +} === added file 'src/acl/Tree.h' --- src/acl/Tree.h 1970-01-01 00:00:00 +0000 +++ src/acl/Tree.h 2013-05-13 22:48:23 +0000 @@ -0,0 +1,44 @@ +#ifndef SQUID_ACL_TREE_H +#define SQUID_ACL_TREE_H + +#include "acl/BoolOps.h" + +namespace Acl { + +/// An ORed set of rules at the top of the ACL expression tree, providing two +/// unique properties: cbdata protection and optional rule actions. +class Tree: public OrNode +{ +public: + /// dumps tuples + /// action.kind is mapped to a string using the supplied conversion table + typedef const char **ActionToString; + wordlist* treeDump(const char *name, const ActionToString &convert) const; + + /// Returns the corresponding action after a successful tree match. + allow_t winningAction() const; + + /// what action to use if no nodes matched + allow_t lastAction() const; + + /// appends and takes control over the rule with a given action + void add(ACL *rule, const allow_t &action); + void add(ACL *rule); ///< same as InnerNode::add() + +protected: + allow_t actionAt(const Nodes::size_type pos) const; + + /// if not empty, contains actions corresponding to InnerNode::nodes + typedef std::vector Actions; + Actions actions; + +private: + // XXX: We should use refcounting instead, but it requires making ACLs + // refcounted as well. Otherwise, async lookups will reach deleted ACLs. + CBDATA_CLASS2(Tree); +}; + + +} // namespace Acl + +#endif /* SQUID_ACL_TREE_H */ === added file 'src/acl/forward.h' --- src/acl/forward.h 1970-01-01 00:00:00 +0000 +++ src/acl/forward.h 2013-05-13 22:48:23 +0000 @@ -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 */ === modified file 'src/adaptation/AccessRule.cc' --- src/adaptation/AccessRule.cc 2012-09-23 09:04:21 +0000 +++ src/adaptation/AccessRule.cc 2013-05-13 22:48:23 +0000 @@ -20,7 +20,7 @@ void Adaptation::AccessRule::parse(ConfigParser &parser) { - aclParseAccessLine(parser, &acl); + aclParseAccessLine("adaptation_access", parser, &acl); } void === modified file 'src/adaptation/AccessRule.h' --- src/adaptation/AccessRule.h 2012-09-21 14:57:30 +0000 +++ src/adaptation/AccessRule.h 2013-05-13 22:48:23 +0000 @@ -1,10 +1,10 @@ #ifndef SQUID_ADAPTATION__ACCESS_RULE_H #define SQUID_ADAPTATION__ACCESS_RULE_H +#include "acl/forward.h" +#include "adaptation/forward.h" #include "SquidString.h" -#include "adaptation/forward.h" -class acl_access; class ConfigParser; namespace Adaptation === modified file 'src/adaptation/Config.h' --- src/adaptation/Config.h 2012-10-26 19:42:31 +0000 +++ src/adaptation/Config.h 2013-05-13 22:48:23 +0000 @@ -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; === modified file 'src/adaptation/icap/Config.h' --- src/adaptation/icap/Config.h 2012-09-01 14:38:36 +0000 +++ src/adaptation/icap/Config.h 2013-05-13 22:48:23 +0000 @@ -36,11 +36,10 @@ #include "event.h" #include "base/AsyncCall.h" +#include "acl/forward.h" #include "adaptation/Config.h" #include "adaptation/icap/ServiceRep.h" -class acl_access; - namespace Adaptation { namespace Icap === modified file 'src/auth/Acl.cc' --- src/auth/Acl.cc 2013-05-13 03:57:03 +0000 +++ src/auth/Acl.cc 2013-05-13 23:32:23 +0000 @@ -58,8 +58,10 @@ 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: === modified file 'src/auth/AclMaxUserIp.cc' --- src/auth/AclMaxUserIp.cc 2013-01-25 16:53:16 +0000 +++ src/auth/AclMaxUserIp.cc 2013-05-13 22:48:23 +0000 @@ -139,8 +139,8 @@ 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 } === modified file 'src/auth/AclProxyAuth.cc' --- src/auth/AclProxyAuth.cc 2013-04-04 06:15:00 +0000 +++ src/auth/AclProxyAuth.cc 2013-05-13 22:48:23 +0000 @@ -92,8 +92,8 @@ 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 @@ { 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 @@ { ACLFilledChecklist *checklist = Filled(static_cast(data)); - assert (checklist->asyncState() == ProxyAuthLookup::Instance()); - if (checklist->auth_user_request == NULL || !checklist->auth_user_request->valid() || checklist->conn() == NULL) { /* credentials could not be checked either way * restart the whole process */ @@ -167,9 +164,7 @@ } } - checklist->asyncInProgress(false); - checklist->changeState (ACLChecklist::NullState::Instance()); - checklist->matchNonBlocking(); + checklist->resumeNonBlockingCheck(ProxyAuthLookup::Instance()); } ACL * === modified file 'src/cache_cf.cc' --- src/cache_cf.cc 2013-05-13 03:57:03 +0000 +++ src/cache_cf.cc 2013-05-13 23:32:23 +0000 @@ -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" @@ -1282,11 +1283,15 @@ *****************************************************************************/ static void +dump_wordlist(StoreEntry * entry, wordlist *words) +{ + for (wordlist *word = words; word; word = words->next) + storeAppendPrintf(entry, "%s ", word->key); +} + +static void dump_acl(StoreEntry * entry, const char *name, ACL * ae) { - wordlist *w; - wordlist *v; - while (ae != NULL) { debugs(3, 3, "dump_acl: " << name << " " << ae->name); storeAppendPrintf(entry, "%s %s %s %s ", @@ -1294,13 +1299,8 @@ 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); @@ -1323,33 +1323,29 @@ 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 @@ -1426,7 +1422,7 @@ 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; @@ -1494,7 +1490,7 @@ l->tos = (tos_t)tos; - aclParseAclList(LegacyParser, &l->aclList); + aclParseAclList(LegacyParser, &l->aclList, token); while (*tail) tail = &(*tail)->next; @@ -1565,7 +1561,7 @@ l->nfmark = mark; - aclParseAclList(LegacyParser, &l->aclList); + aclParseAclList(LegacyParser, &l->aclList, token); while (*tail) tail = &(*tail)->next; @@ -1623,7 +1619,7 @@ parse_b_int64_t(&l->size); - aclParseAclList(LegacyParser, &l->aclList); + aclParseAclList(LegacyParser, &l->aclList, l->size); while (*tail) tail = &(*tail)->next; @@ -1760,7 +1756,10 @@ 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 @@ -2523,7 +2522,9 @@ return; } - aclParseAccessLine(LegacyParser, &p->access); + std::string directive = "peer_access "; + directive += host; + aclParseAccessLine(directive.c_str(), LegacyParser, &p->access); } static void @@ -4056,7 +4057,7 @@ 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; @@ -4105,7 +4106,7 @@ if (cl->type == Log::Format::CLF_UNKNOWN) setLogformat(cl, "squid", true); - aclParseAclList(LegacyParser, &cl->aclList); + aclParseAclList(LegacyParser, &cl->aclList, cl->filename); while (*logs) logs = &(*logs)->next; @@ -4507,7 +4508,7 @@ return; } - aclParseAclList(LegacyParser, &ca->aclList); + aclParseAclList(LegacyParser, &ca->aclList, al); while (*cert_adapt) cert_adapt = &(*cert_adapt)->next; @@ -4561,7 +4562,7 @@ return; } - aclParseAclList(LegacyParser, &cs->aclList); + aclParseAclList(LegacyParser, &cs->aclList, al); while (*cert_sign) cert_sign = &(*cert_sign)->next; @@ -4647,31 +4648,30 @@ sslBumpCfgRr::lastDeprecatedRule = Ssl::bumpEnd; } - acl_access *A = new acl_access; - A->allow = allow_t(ACCESS_ALLOWED); + allow_t action = allow_t(ACCESS_ALLOWED); if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpClientFirst]) == 0) { - A->allow.kind = Ssl::bumpClientFirst; + action.kind = Ssl::bumpClientFirst; bumpCfgStyleNow = bcsNew; } else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpServerFirst]) == 0) { - A->allow.kind = Ssl::bumpServerFirst; + action.kind = Ssl::bumpServerFirst; bumpCfgStyleNow = bcsNew; } else if (strcmp(bm, Ssl::BumpModeStr[Ssl::bumpNone]) == 0) { - A->allow.kind = Ssl::bumpNone; + action.kind = Ssl::bumpNone; bumpCfgStyleNow = bcsNew; } else if (strcmp(bm, "allow") == 0) { debugs(3, DBG_CRITICAL, "SECURITY NOTICE: auto-converting deprecated " "\"ssl_bump allow \" to \"ssl_bump client-first \" which " "is usually inferior to the newer server-first " "bumping mode. Update your ssl_bump rules."); - A->allow.kind = Ssl::bumpClientFirst; + action.kind = Ssl::bumpClientFirst; bumpCfgStyleNow = bcsOld; sslBumpCfgRr::lastDeprecatedRule = Ssl::bumpClientFirst; } else if (strcmp(bm, "deny") == 0) { debugs(3, DBG_CRITICAL, "WARNING: auto-converting deprecated " "\"ssl_bump deny \" to \"ssl_bump none \". Update " "your ssl_bump rules."); - A->allow.kind = Ssl::bumpNone; + action.kind = Ssl::bumpNone; bumpCfgStyleNow = bcsOld; sslBumpCfgRr::lastDeprecatedRule = Ssl::bumpNone; } else { @@ -4689,22 +4689,26 @@ bumpCfgStyleLast = bumpCfgStyleNow; - aclParseAclList(LegacyParser, &A->aclList); - - acl_access *B, **T; - for (B = *ssl_bump, T = ssl_bump; B; T = &B->next, B = B->next); - *T = A; + 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); + } + + (*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); } } @@ -4758,7 +4762,8 @@ } hwa.valueFormat = nlf; } - aclParseAclList(LegacyParser, &hwa.aclList); + + aclParseAclList(LegacyParser, &hwa.aclList, (hwa.fieldName + ':' + hwa.fieldValue).c_str()); (*headers)->push_back(hwa); } === modified file 'src/cf.data.pre' --- src/cf.data.pre 2013-05-13 03:57:03 +0000 +++ src/cf.data.pre 2013-05-13 23:32:23 +0000 @@ -1076,6 +1076,29 @@ # 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 === modified file 'src/cf_gen.cc' --- src/cf_gen.cc 2013-04-23 12:35:44 +0000 +++ src/cf_gen.cc 2013-05-13 22:48:23 +0000 @@ -631,6 +631,7 @@ 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 @@ fout << "parse_" << type << "(&" << loc << (array_flag ? "[0]" : "") << ");"; } fout << std::endl; + fout << " cfg_directive = NULL;" << std::endl; if (ifdef.size()) { fout << "#else" << std::endl << === modified file 'src/client_side_request.cc' --- src/client_side_request.cc 2013-05-13 03:57:03 +0000 +++ src/client_side_request.cc 2013-05-13 23:32:23 +0000 @@ -780,8 +780,7 @@ 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 = ""; === modified file 'src/client_side_request.h' --- src/client_side_request.h 2013-03-16 04:57:43 +0000 +++ src/client_side_request.h 2013-05-13 22:48:23 +0000 @@ -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; === modified file 'src/defines.h' --- src/defines.h 2013-01-13 01:13:10 +0000 +++ src/defines.h 2013-05-13 22:48:23 +0000 @@ -41,7 +41,6 @@ #define BUFSIZ 4096 /* make unreasonable guess */ #endif -#define ACL_NAME_SZ 32 #define BROWSERNAMELEN 128 #define ACL_SUNDAY 0x01 === modified file 'src/external_acl.cc' --- src/external_acl.cc 2013-04-29 13:31:05 +0000 +++ src/external_acl.cc 2013-05-13 22:48:23 +0000 @@ -777,14 +777,17 @@ 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 @@ 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 @@ } } - external_acl_message = "MISSING REQUIRED INFORMATION"; - if (!entry) { debugs(82, 9, HERE << "No helper entry available"); #if USE_AUTH @@ -820,7 +821,7 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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 @@ assert(acl); ACLExternal *me = dynamic_cast (acl); assert (me); - checklist->asyncInProgress(true); ACLExternal::ExternalAclLookup(checklist, me); } @@ -1552,9 +1555,7 @@ { ACLFilledChecklist *checklist = Filled(static_cast(data)); checklist->extacl_entry = cbdataReference((external_acl_entry *)result); - checklist->asyncInProgress(false); - checklist->changeState (ACLChecklist::NullState::Instance()); - checklist->matchNonBlocking(); + checklist->resumeNonBlockingCheck(ExternalACLLookup::Instance()); } /* This registers "external" in the registry. To do dynamic definitions === modified file 'src/globals.h' --- src/globals.h 2013-02-17 01:55:00 +0000 +++ src/globals.h 2013-05-13 22:48:23 +0000 @@ -47,6 +47,8 @@ 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; /* "-" */ === modified file 'src/ident/AclIdent.cc' --- src/ident/AclIdent.cc 2012-09-01 14:38:36 +0000 +++ src/ident/AclIdent.cc 2013-05-13 22:48:23 +0000 @@ -89,14 +89,18 @@ } 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 @@ // 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 @@ IdentLookup::LookupDone(const char *ident, void *data) { ACLFilledChecklist *checklist = Filled(static_cast(data)); - assert(checklist->asyncState() == IdentLookup::Instance()); if (ident) { xstrncpy(checklist->rfc931, ident, USER_IDENT_SZ); @@ -156,9 +158,7 @@ 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 */ === modified file 'src/ip/QosConfig.h' --- src/ip/QosConfig.h 2012-10-04 00:23:44 +0000 +++ src/ip/QosConfig.h 2013-05-13 22:48:23 +0000 @@ -1,6 +1,7 @@ #ifndef SQUID_QOSCONFIG_H #define SQUID_QOSCONFIG_H +#include "acl/forward.h" #include "hier_code.h" #include "ip/forward.h" @@ -16,7 +17,6 @@ #include #endif -class ACLList; class fde; // TODO: move to new ACL framework === modified file 'src/log/CustomLog.h' --- src/log/CustomLog.h 2013-05-11 20:59:44 +0000 +++ src/log/CustomLog.h 2013-05-13 23:32:23 +0000 @@ -29,9 +29,9 @@ * */ //#include "format/Format.h" +#include "acl/forward.h" #include "log/Formats.h" -class ACLList; class Logfile; namespace Format { === modified file 'src/ssl/ProxyCerts.h' --- src/ssl/ProxyCerts.h 2012-10-04 00:23:44 +0000 +++ src/ssl/ProxyCerts.h 2013-05-13 22:48:23 +0000 @@ -30,7 +30,7 @@ */ #if USE_SSL -class ACLList; +#include "acl/forward.h" class sslproxy_cert_sign { # Begin bundle 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