/*
- * Copyright (C) 1996-2014 The Squid Software Foundation and contributors
+ * Copyright (C) 1996-2017 The Squid Software Foundation and contributors
*
* Squid software is distributed under GPLv2+ license and includes
* contributions from numerous individuals and organizations.
#include "ConfigParser.h"
#include "Debug.h"
#include "dlink.h"
+#include "fatal.h"
#include "globals.h"
#include "profiler/Profiler.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};
const char *AclMatchedName = NULL;
-bool ACLFlags::supported(const ACLFlag f) const
+ACLFlags::FlagsTokenizer::FlagsTokenizer(): tokPos(NULL) { }
+
+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];
+}
+
+SBuf
+ACLFlags::FlagsTokenizer::getParameter() const
+{
+ return hasParameter() ? SBuf(&tokPos[2]) : SBuf();
+}
+
+bool
+ACLFlags::FlagsTokenizer::needNextToken() const
+{
+ 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 true;
- return (supported_.find(f) != std::string::npos);
+ 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;
+}
+
+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()
{
- char *nextToken;
- while ((nextToken = ConfigParser::PeekAtToken()) != NULL && nextToken[0] == '-') {
- (void)ConfigParser::NextToken(); //Get token from cfg line
- //if token is the "--" break flag
- if (strcmp(nextToken, "--") == 0)
+ FlagsTokenizer tokenizer;
+ ACLFlag flag('\0');
+ while ((flag = tokenizer.nextFlag())) {
+ switch (flagStatus(flag))
+ {
+ case notSupported:
+ abortFlags("Flag '" << flag << "' not supported");
break;
-
- for (const char *flg = nextToken+1; *flg!='\0'; flg++ ) {
- if (supported(*flg)) {
- makeSet(*flg);
- } else {
- debugs(28, 0, HERE << "Flag '" << *flg << "' not supported");
- self_destruct();
+ 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))
+ if ( isSet(ACL_F_REGEX_CASE)) {
ConfigParser::TokenPutBack("-i");
+ makeUnSet('i');
+ }
+}
+
+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;
+}
+
+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_;
}
const char *
}
void *
-ACL::operator new (size_t byteCount)
+ACL::operator new (size_t)
{
fatal ("unusable ACL::new");
return (void *)1;
}
void
-ACL::operator delete (void *address)
+ACL::operator delete (void *)
{
fatal ("unusable ACL::delete");
}
}
ACL::ACL() :
- cfgline(NULL),
- next(NULL),
- registered(false)
+ cfgline(nullptr),
+ next(nullptr),
+ registered(false)
+{
+ *name = 0;
+}
+
+ACL::ACL(const ACLFlag flgs[]) :
+ cfgline(NULL),
+ next(NULL),
+ flags(flgs),
+ registered(false)
{
*name = 0;
}
AclMatchedName = name;
int result = 0;
- if (!checklist->hasRequest() && requiresRequest()) {
+ if (!checklist->hasAle() && requiresAle()) {
+ debugs(28, DBG_IMPORTANT, "WARNING: " << name << " ACL is used in " <<
+ "context without an ALE state. Assuming mismatch.");
+ } else 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 {
+ // make sure the ALE has as much data as possible
+ if (requiresAle())
+ checklist->syncAle();
+
// have to cast because old match() API is missing const
result = const_cast<ACL*>(this)->match(checklist);
}
}
theType = "localport";
debugs(28, DBG_IMPORTANT, "UPGRADE: ACL 'myport' type is has been renamed to 'localport' and matches the port the client connected to.");
+ } else if (strcmp(theType, "proto") == 0 && strcmp(aclname, "manager") == 0) {
+ // 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)) {
* Here we set AclMatchedName in case we need to use it in a
* warning message in aclDomainCompare().
*/
- AclMatchedName = A->name; /* ugly */
+ AclMatchedName = A->name; /* ugly */
A->flags.parseFlags();
/*
* Clear AclMatchedName from our temporary hack
*/
- AclMatchedName = NULL; /* ugly */
+ AclMatchedName = NULL; /* ugly */
if (!new_acl)
return;
/* ACL result caching routines */
int
-ACL::matchForCache(ACLChecklist *checklist)
+ACL::matchForCache(ACLChecklist *)
{
/* This is a fatal to ensure that cacheMatchAcl calls are _only_
* made for supported acl types */
fatal("aclCacheMatchAcl: unknown or unexpected ACL type");
- return 0; /* NOTREACHED */
+ return 0; /* NOTREACHED */
}
/*
link = link->next;
}
- auth_match = new acl_proxy_auth_match_cache();
- auth_match->matchrv = matchForCache (checklist);
- auth_match->acl_data = this;
+ auth_match = new acl_proxy_auth_match_cache(matchForCache(checklist), this);
dlinkAddTail(auth_match, &auth_match->link, cache);
debugs(28, 4, "ACL::cacheMatchAcl: miss for '" << name << "'. Adding result " << auth_match->matchrv);
return auth_match->matchrv;
}
}
+bool
+ACL::requiresAle() const
+{
+ return false;
+}
+
bool
ACL::requiresReply() const
{
ACL::~ACL()
{
- debugs(28, 3, "ACL::~ACL: '" << cfgline << "'");
+ debugs(28, 3, "freeing ACL " << name);
safe_free(cfgline);
AclMatchedName = NULL; // in case it was pointing to our name
}
a = a->next;
}
}
+