/*
- * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
+ * Copyright (C) 1996-2020 The Squid Software Foundation and contributors
*
* Squid software is distributed under GPLv2+ license and includes
* contributions from numerous individuals and organizations.
#include "acl/Acl.h"
#include "acl/Checklist.h"
#include "acl/Gadgets.h"
+#include "acl/Options.h"
#include "anyp/PortCfg.h"
#include "cache_cf.h"
#include "ConfigParser.h"
#include "Debug.h"
-#include "dlink.h"
#include "fatal.h"
#include "globals.h"
#include "profiler/Profiler.h"
+#include "sbuf/List.h"
+#include "sbuf/Stream.h"
#include "SquidConfig.h"
-#include <vector>
-
-#define abortFlags(CONTENT) \
- do { \
- debugs(28, 0, CONTENT); \
- self_destruct(); \
- } while (0)
-
-const ACLFlag ACLFlags::NoFlags[1] = {ACL_F_END};
+#include <algorithm>
+#include <map>
const char *AclMatchedName = NULL;
-ACLFlags::FlagsTokenizer::FlagsTokenizer(): tokPos(NULL) { }
+namespace Acl {
-ACLFlag
-ACLFlags::FlagsTokenizer::nextFlag()
-{
- if (needNextToken()) {
- if (!nextToken())
- return 0;
- } else
- ++tokPos;
- return *tokPos;
-}
-
-bool
-ACLFlags::FlagsTokenizer::hasParameter() const
-{
- return tokPos && tokPos[0] && tokPos[1] == '=' && tokPos[2];
-}
+/// ACL type name comparison functor
+class TypeNameCmp {
+public:
+ bool operator()(TypeName a, TypeName b) const { return strcmp(a, b) < 0; }
+};
-SBuf
-ACLFlags::FlagsTokenizer::getParameter() const
-{
- return hasParameter() ? SBuf(&tokPos[2]) : SBuf();
-}
+/// ACL makers indexed by ACL type name
+typedef std::map<TypeName, Maker, TypeNameCmp> Makers;
-bool
-ACLFlags::FlagsTokenizer::needNextToken() const
+/// registered ACL Makers
+static Makers &
+TheMakers()
{
- return !tokPos || !tokPos[0] || !tokPos[1] || tokPos[1] == '=';
-}
-
-bool
-ACLFlags::FlagsTokenizer::nextToken()
-{
- char *t = ConfigParser::PeekAtToken();
- if (t == NULL || t[0] != '-' || !t[1])
- return false;
- (void)ConfigParser::NextQuotedToken();
- if (strcmp(t, "--") == 0)
- return false;
- tokPos = t + 1;
- return true;
-}
-
-ACLFlags::~ACLFlags()
-{
- delete delimiters_;
-}
-
-ACLFlags::Status
-ACLFlags::flagStatus(const ACLFlag f) const
-{
- if (f == ACL_F_REGEX_CASE)
- return noParameter;
- if (f == ACL_F_SUBSTRING)
- return parameterOptional;
- if (supported_.find(f) != std::string::npos)
- return noParameter;
- return notSupported;
-}
-
-bool
-ACLFlags::parameterSupported(const ACLFlag f, const SBuf &val) const
-{
- if (f == ACL_F_SUBSTRING)
- return val.findFirstOf(CharacterSet::ALPHA + CharacterSet::DIGIT) == SBuf::npos;
- return true;
+ static Makers Registry;
+ return Registry;
}
-void
-ACLFlags::makeSet(const ACLFlag f, const SBuf ¶m)
-{
- flags_ |= flagToInt(f);
- if (!param.isEmpty())
- flagParameters_[f].append(param);
-}
-
-void
-ACLFlags::makeUnSet(const ACLFlag f)
-{
- flags_ &= ~flagToInt(f);
- flagParameters_[f].clear();
-}
-
-void
-ACLFlags::parseFlags()
+/// creates an ACL object of the named (and already registered) ACL child type
+static
+ACL *
+Make(TypeName typeName)
{
- FlagsTokenizer tokenizer;
- ACLFlag flag('\0');
- while ((flag = tokenizer.nextFlag())) {
- switch (flagStatus(flag))
- {
- case notSupported:
- abortFlags("Flag '" << flag << "' not supported");
- break;
- case noParameter:
- makeSet(flag);
- break;
- case parameterRequired:
- if (!tokenizer.hasParameter()) {
- abortFlags("Flag '" << flag << "' must have a parameter");
- break;
- }
- case parameterOptional:
- SBuf param;
- if (tokenizer.hasParameter()) {
- param = tokenizer.getParameter();
- if (!parameterSupported(flag, param))
- abortFlags("Parameter '" << param << "' for flag '" << flag << "' not supported");
- }
- makeSet(flag, param);
- break;
- }
- }
-
- /*Regex code needs to parse -i file*/
- if ( isSet(ACL_F_REGEX_CASE)) {
- ConfigParser::TokenPutBack("-i");
- makeUnSet('i');
+ const auto pos = TheMakers().find(typeName);
+ if (pos == TheMakers().end()) {
+ debugs(28, DBG_CRITICAL, "FATAL: Invalid ACL type '" << typeName << "'");
+ self_destruct();
+ assert(false); // not reached
}
-}
-SBuf
-ACLFlags::parameter(const ACLFlag f) const
-{
- assert(static_cast<uint32_t>(f - 'A') < FlagIndexMax);
- auto p = flagParameters_.find(f);
- return p == flagParameters_.end() ? SBuf() : p->second;
+ ACL *result = (pos->second)(pos->first);
+ debugs(28, 4, typeName << '=' << result);
+ assert(result);
+ return result;
}
-const CharacterSet *
-ACLFlags::delimiters()
-{
- if (isSet(ACL_F_SUBSTRING) && !delimiters_) {
- static const SBuf defaultParameter(",");
- SBuf rawParameter = parameter(ACL_F_SUBSTRING);
- if (rawParameter.isEmpty())
- rawParameter = defaultParameter;
- delimiters_ = new CharacterSet("ACLFlags::delimiters", rawParameter.c_str());
- }
- return delimiters_;
-}
+} // namespace Acl
-const char *
-ACLFlags::flagsStr() const
+void
+Acl::RegisterMaker(TypeName typeName, Maker maker)
{
- static char buf[64];
- if (flags_ == 0)
- return "";
-
- char *s = buf;
- *s++ = '-';
- for (ACLFlag f = 'A'; f <= 'z'; f++) {
- // ACL_F_REGEX_CASE (-i) flag handled by ACLRegexData class, ignore
- if (isSet(f) && f != ACL_F_REGEX_CASE)
- *s++ = f;
- }
- *s = '\0';
- return buf;
+ assert(typeName);
+ assert(*typeName);
+ TheMakers().emplace(typeName, maker);
}
void *
return NULL;
}
-ACL *
-ACL::Factory (char const *type)
-{
- ACL *result = Prototype::Factory (type);
-
- if (!result)
- fatal ("Unknown acl type in ACL::Factory");
-
- return result;
-}
-
ACL::ACL() :
cfgline(nullptr),
next(nullptr),
*name = 0;
}
-ACL::ACL(const ACLFlag flgs[]) :
- cfgline(NULL),
- next(NULL),
- flags(flgs),
- registered(false)
-{
- *name = 0;
-}
-
bool ACL::valid () const
{
return true;
} else {
// make sure the ALE has as much data as possible
if (requiresAle())
- checklist->syncAle();
+ checklist->verifyAle();
// have to cast because old match() API is missing const
result = const_cast<ACL*>(this)->match(checklist);
// ACL manager is now a built-in and has a different type.
debugs(28, DBG_PARSE_NOTE(DBG_IMPORTANT), "UPGRADE: ACL 'manager' is now a built-in ACL. Remove it from your config file.");
return; // ignore the line
- }
-
- if (!Prototype::Registered(theType)) {
- debugs(28, DBG_CRITICAL, "FATAL: Invalid ACL type '" << theType << "'");
- // XXX: make this an ERROR and skip the ACL creation. We *may* die later when its use is attempted. Or may not.
- parser.destruct();
- return;
+ } else if (strcmp(theType, "clientside_mark") == 0) {
+ debugs(28, DBG_IMPORTANT, "UPGRADE: ACL 'clientside_mark' type has been renamed to 'client_connection_mark'.");
+ theType = "client_connection_mark";
}
if ((A = FindByName(aclname)) == NULL) {
debugs(28, 3, "aclParseAclLine: Creating ACL '" << aclname << "'");
- A = ACL::Factory(theType);
+ A = Acl::Make(theType);
A->context(aclname, config_input_line);
new_acl = 1;
} else {
*/
AclMatchedName = A->name; /* ugly */
- A->flags.parseFlags();
+ A->parseFlags();
/*split the function here */
A->parse();
return false;
}
+void
+ACL::parseFlags()
+{
+ // ACL kids that carry ACLData which supports parameter flags override this
+ Acl::ParseFlags(options(), Acl::NoFlags());
+}
+
+SBufList
+ACL::dumpOptions()
+{
+ SBufList result;
+ const auto &myOptions = options();
+ // optimization: most ACLs do not have myOptions
+ // this check also works around dump_SBufList() adding ' ' after empty items
+ if (!myOptions.empty()) {
+ SBufStream stream;
+ stream << myOptions;
+ const SBuf optionsImage = stream.buf();
+ if (!optionsImage.isEmpty())
+ result.push_back(optionsImage);
+ }
+ return result;
+}
+
/* ACL result caching routines */
int
AclMatchedName = NULL; // in case it was pointing to our name
}
-ACL::Prototype::Prototype() : prototype (NULL), typeString (NULL) {}
-
-ACL::Prototype::Prototype (ACL const *aPrototype, char const *aType) : prototype (aPrototype), typeString (aType)
-{
- registerMe ();
-}
-
-std::vector<ACL::Prototype const *> * ACL::Prototype::Registry;
-void *ACL::Prototype::Initialized;
-
-bool
-ACL::Prototype::Registered(char const *aType)
-{
- debugs(28, 7, "ACL::Prototype::Registered: invoked for type " << aType);
-
- for (iterator i = Registry->begin(); i != Registry->end(); ++i)
- if (!strcmp (aType, (*i)->typeString)) {
- debugs(28, 7, "ACL::Prototype::Registered: yes");
- return true;
- }
-
- debugs(28, 7, "ACL::Prototype::Registered: no");
- return false;
-}
-
-void
-ACL::Prototype::registerMe ()
-{
- if (!Registry || (Initialized != ((char *)Registry - 5)) ) {
- /* TODO: extract this */
- /* Not initialised */
- Registry = new std::vector<ACL::Prototype const *>;
- Initialized = (char *)Registry - 5;
- }
-
- if (Registered (typeString))
- fatalf ("Attempt to register %s twice", typeString);
-
- Registry->push_back (this);
-}
-
-ACL::Prototype::~Prototype()
-{
- // TODO: unregister me
-}
-
-ACL *
-ACL::Prototype::Factory (char const *typeToClone)
-{
- debugs(28, 4, "ACL::Prototype::Factory: cloning an object for type '" << typeToClone << "'");
-
- for (iterator i = Registry->begin(); i != Registry->end(); ++i)
- if (!strcmp (typeToClone, (*i)->typeString)) {
- ACL *A = (*i)->prototype->clone();
- A->flags = (*i)->prototype->flags;
- return A;
- }
-
- debugs(28, 4, "ACL::Prototype::Factory: cloning failed, no type '" << typeToClone << "' available");
-
- return NULL;
-}
-
void
ACL::Initialize()
{