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"
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_;
*
*/
+#include "acl/forward.h"
#include "enums.h"
#include "icp_opcode.h"
#include "ip/Address.h"
#include <openssl/ssl.h>
#endif
-class acl_access;
class CachePeerDomainList;
class NeighborTypeDomainList;
class PeerDigest;
}
--pool;
- aclParseAccessLine(parser, &pools[pool].access);
+ aclParseAccessLine("client_delay_access", parser, &pools[pool].access);
}
void ClientDelayConfig::clean()
#define SQUID_CLIENTDELAYCONFIG_H
#include "Array.h"
+#include "acl/forward.h"
class StoreEntry;
-class acl_access;
class ConfigParser;
/// \ingroup DelayPoolsAPI
}
--pool;
- aclParseAccessLine(parser, &DelayPools::delay_data[pool].access);
+ aclParseAccessLine("delay_access", parser, &DelayPools::delay_data[pool].access);
}
void
#if USE_DELAY_POOLS
#include "CompositePoolNode.h"
+#include "acl/forward.h"
class StoreEntry;
class CommonPool;
-class acl_access;
-
/// \ingroup DelayPoolsAPI
class DelayPool
{
#ifndef SQUID_HTTPHEADERTOOLS_H
#define SQUID_HTTPHEADERTOOLS_H
+#include "acl/forward.h"
#include "format/Format.h"
#include "HttpHeader.h"
#include "typedefs.h"
#include <string>
#endif
-class acl_access;
-class ACLList;
class HeaderWithAcl;
class HttpHeader;
class HttpHeaderFieldInfo;
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) {
#ifndef SQUID_NOTES_H
#define SQUID_NOTES_H
+#include "acl/forward.h"
#include "Array.h"
#include "base/RefCount.h"
#include "CbDataList.h"
class HttpRequest;
class HttpReply;
-class ACLList;
/**
* Used to store a note configuration. The notes are custom key:value
*
*/
-#include "acl/AclAddress.h"
+#include "acl/forward.h"
#include "base/RefCount.h"
#include "ClientDelayConfig.h"
#include "DelayConfig.h"
class sslproxy_cert_adapt;
#endif
-class acl_access;
-class AclSizeLimit;
-class AclDenyInfoList;
namespace Mgr
{
class ActionPasswordList;
/*
* DEBUG: section 28 Access Control
- * AUTHOR: Duane Wessels
*
* SQUID Web Proxy Cache http://www.squid-cache.org/
* ----------------------------------------------------------
#include "Debug.h"
#include "dlink.h"
#include "globals.h"
+#include "profiler/Profiler.h"
#include "SquidConfig.h"
const ACLFlag ACLFlags::NoFlags[1] = {ACL_F_END};
ACL::ACL() :
cfgline(NULL),
- next(NULL)
+ next(NULL),
+ registered(false)
{
*name = 0;
}
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)
{
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) ) {
}
/* append */
+ assert(head && *head == Config.aclList);
+ A->registered = true;
+
while (*head)
head = &(*head)->next;
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
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 */
/*********************/
{
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) {}
#ifndef SQUID_ACL_H
#define SQUID_ACL_H
+#include "acl/forward.h"
#include "Array.h"
#include "cbdata.h"
#include "defines.h"
#endif
class ConfigParser;
-class ACLChecklist;
-class ACLList;
typedef char ACLFlag;
// ACLData Flags
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
{
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);
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:
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
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
{
*
*/
-#include "defines.h"
+#include "acl/forward.h"
/// list of name-based ACLs. Currently a POD.
class AclNameList
*
*/
-class ACLList;
+#include "acl/forward.h"
+
/// representation of a class of Size-limit ACLs
// a POD. TODO: convert to new ACL framework
class AclSizeLimit
--- /dev/null
+#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);
+}
--- /dev/null
+#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 */
--- /dev/null
+#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();
+}
--- /dev/null
+#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 */
#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"
/* 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 *
/*
* 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.
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
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
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())
{
}
debugs(28, 4, "ACLChecklist::~ACLChecklist: destroyed " << this);
}
-void
-ACLChecklist::AsyncState::changeState (ACLChecklist *checklist, AsyncState *newState) const
-{
- checklist->changeState(newState);
-}
-
ACLChecklist::NullState *
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;
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();
}
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()) {
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();
/// 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;
markFinished(implicitRuleAnswer, "implicit rule won");
}
-bool
-ACLChecklist::checking() const
-{
- return checking_;
-}
-
-void
-ACLChecklist::checking (bool const newValue)
-{
- checking_ = newValue;
-}
-
bool
ACLChecklist::callerGone()
{
#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 *);
public:
virtual void checkForAsync(ACLChecklist *) const = 0;
virtual ~AsyncState() {}
-
- protected:
- void changeState (ACLChecklist *, AsyncState *) const;
};
class NullState : public AsyncState
*
* If there are no ACLs to check at all, the result becomes ACCESS_ALLOWED.
*/
- allow_t const & fastCheck(const ACLList * list);
+ allow_t const & fastCheck(const Acl::Tree *list);
+
+ /// If slow lookups are allowed, switches into "async in progress" state.
+ /// Otherwise, returns false; the caller is expected to handle the failure.
+ bool goAsync(AsyncState *);
+
+ /// Matches (or resumes matching of) a child node while maintaning
+ /// resumption breadcrumbs if a [grand]child node goes async.
+ bool matchChild(const Acl::InnerNode *parent, Acl::Nodes::const_iterator pos, const ACL *child);
- // whether the last checked ACL of the current rule needs
- // an async operation to determine whether there was a match
- bool asyncNeeded() const;
- bool asyncInProgress() const;
- void asyncInProgress(bool const);
+ /// Whether we should continue to match tree nodes or stop/pause.
+ bool keepMatching() const { return !finished() && !asyncInProgress(); }
/// whether markFinished() was called
- bool finished() const;
+ bool finished() const { return finished_; }
+ /// async call has been started and has not finished (or failed) yet
+ bool asyncInProgress() const { return asyncStage_ != asyncNone; }
/// called when no more ACLs should be checked; sets the final answer and
/// prints a debugging message explaining the reason for that answer
void markFinished(const allow_t &newAnswer, const char *reason);
const allow_t ¤tAnswer() const { return allow_; }
- void changeState(AsyncState *);
- AsyncState *asyncState() const;
-
// XXX: ACLs that need request or reply have to use ACLFilledChecklist and
// should do their own checks so that we do not have to povide these two
// for ACL::checklistMatches to use
/// 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 */
DestinationDomainLookup::checkForAsync(ACLChecklist *cl) const
{
ACLFilledChecklist *checklist = Filled(cl);
- checklist->asyncInProgress(true);
fqdncache_nbgethostbyaddr(checklist->dst_addr, LookupDone, checklist);
}
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
} 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");
} 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_;
DestinationIPLookup::checkForAsync(ACLChecklist *cl)const
{
ACLFilledChecklist *checklist = Filled(cl);
- checklist->asyncInProgress(true);
ipcache_nbgethostbyname(checklist->request->GetHost(), LookupDone, checklist);
}
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 *
#define SQUID_ACLFILLED_CHECKLIST_H
#include "acl/Checklist.h"
+#include "acl/forward.h"
#include "ip/Address.h"
#if USE_AUTH
#include "auth/UserRequest.h"
#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"
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;
}
}
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;
}
/*********************/
}
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)
#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;
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
--- /dev/null
+#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;
+}
--- /dev/null
+#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 */
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.
TimeData.h \
Asn.cc \
Asn.h \
+ AllOf.cc \
+ AllOf.h \
+ AnyOf.cc \
+ AnyOf.h \
Browser.cc \
Browser.h \
DestinationAsn.h \
void
SourceDomainLookup::checkForAsync(ACLChecklist *checklist) const
{
- checklist->asyncInProgress(true);
fqdncache_nbgethostbyaddr(Filled(checklist)->src_addr, LookupDone, checklist);
}
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
} 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");
--- /dev/null
+#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;
+}
--- /dev/null
+#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 */
--- /dev/null
+#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 */
void
Adaptation::AccessRule::parse(ConfigParser &parser)
{
- aclParseAccessLine(parser, &acl);
+ aclParseAccessLine("adaptation_access", parser, &acl);
}
void
#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
#define SQUID_ADAPTATION__CONFIG_H
#include "event.h"
+#include "acl/forward.h"
#include "acl/Gadgets.h"
#include "base/AsyncCall.h"
#include "adaptation/forward.h"
#include "Notes.h"
#include "SquidString.h"
-class acl_access;
class ConfigParser;
class HttpRequest;
class HttpReply;
#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
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:
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
}
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
}
{
ACLFilledChecklist *checklist = Filled(cl);
- checklist->asyncInProgress(true);
debugs(28, 3, HERE << "checking password via authenticator");
/* make sure someone created auth_user_request for us */
{
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 */
}
}
- checklist->asyncInProgress(false);
- checklist->changeState (ACLChecklist::NullState::Instance());
- checklist->matchNonBlocking();
+ checklist->resumeNonBlockingCheck(ProxyAuthLookup::Instance());
}
ACL *
#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"
*****************************************************************************/
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 ",
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);
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
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;
l->tos = (tos_t)tos;
- aclParseAclList(LegacyParser, &l->aclList);
+ aclParseAclList(LegacyParser, &l->aclList, token);
while (*tail)
tail = &(*tail)->next;
l->nfmark = mark;
- aclParseAclList(LegacyParser, &l->aclList);
+ aclParseAclList(LegacyParser, &l->aclList, token);
while (*tail)
tail = &(*tail)->next;
parse_b_int64_t(&l->size);
- aclParseAclList(LegacyParser, &l->aclList);
+ aclParseAclList(LegacyParser, &l->aclList, l->size);
while (*tail)
tail = &(*tail)->next;
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
return;
}
- aclParseAccessLine(LegacyParser, &p->access);
+ std::string directive = "peer_access ";
+ directive += host;
+ aclParseAccessLine(directive.c_str(), LegacyParser, &p->access);
}
static void
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;
return;
}
- aclParseAclList(LegacyParser, &cl->aclList);
+ aclParseAclList(LegacyParser, &cl->aclList, filename);
while (*logs)
logs = &(*logs)->next;
return;
}
- aclParseAclList(LegacyParser, &ca->aclList);
+ aclParseAclList(LegacyParser, &ca->aclList, al);
while (*cert_adapt)
cert_adapt = &(*cert_adapt)->next;
return;
}
- aclParseAclList(LegacyParser, &cs->aclList);
+ aclParseAclList(LegacyParser, &cs->aclList, al);
while (*cert_sign)
cert_sign = &(*cert_sign)->next;
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 {
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);
}
}
}
hwa.valueFormat = nlf;
}
- aclParseAclList(LegacyParser, &hwa.aclList);
+
+ aclParseAclList(LegacyParser, &hwa.aclList, (hwa.fieldName + ':' + hwa.fieldValue).c_str());
(*headers)->push_back(hwa);
}
# 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
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";
fout << "parse_" << type << "(&" << loc << (array_flag ? "[0]" : "") << ");";
}
fout << std::endl;
+ fout << " cfg_directive = NULL;" << std::endl;
if (ifdef.size()) {
fout <<
"#else" << std::endl <<
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>";
#ifndef SQUID_CLIENTSIDEREQUEST_H
#define SQUID_CLIENTSIDEREQUEST_H
+#include "acl/forward.h"
#include "HttpHeader.h"
#include "clientStream.h"
#include "client_side.h"
class HttpMsg;
#endif
-class acl_access;
-class ACLFilledChecklist;
class ClientRequestContext;
class ConnStateData;
class MemObject;
#define BUFSIZ 4096 /* make unreasonable guess */
#endif
-#define ACL_NAME_SZ 32
#define BROWSERNAMELEN 128
#define ACL_SUNDAY 0x01
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.
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);
}
}
- external_acl_message = "MISSING REQUIRED INFORMATION";
-
if (!entry) {
debugs(82, 9, HERE << "No helper entry available");
#if USE_AUTH
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 */
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 {
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
}
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;
}
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 << "'");
assert(acl);
ACLExternal *me = dynamic_cast<ACLExternal *> (acl);
assert (me);
- checklist->asyncInProgress(true);
ACLExternal::ExternalAclLookup(checklist, me);
}
{
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
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; /* "-" */
} 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 *
// 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);
}
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);
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 */
#ifndef SQUID_QOSCONFIG_H
#define SQUID_QOSCONFIG_H
+#include "acl/forward.h"
#include "hier_code.h"
#include "ip/forward.h"
#include <limits>
#endif
-class ACLList;
class fde;
// TODO: move to new ACL framework
*
*/
//#include "format/Format.h"
+#include "acl/forward.h"
#include "log/Formats.h"
-class ACLList;
class Logfile;
namespace Format
{
*/
#if USE_SSL
-class ACLList;
+#include "acl/forward.h"
class sslproxy_cert_sign
{