]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - src/acl/Acl.cc
Source Format Enforcement (#1234)
[thirdparty/squid.git] / src / acl / Acl.cc
index dcbc5208896347aae6ad08f690fe6a183b0583ee..5972585e5b280904d1b4b896976e2ac7619257e5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
+ * Copyright (C) 1996-2023 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 "debug/Stream.h"
 #include "fatal.h"
 #include "globals.h"
-#include "profiler/Profiler.h"
+#include "sbuf/List.h"
+#include "sbuf/Stream.h"
 #include "SquidConfig.h"
 
-#include <vector>
+#include <algorithm>
+#include <map>
 
-const ACLFlag ACLFlags::NoFlags[1] = {ACL_F_END};
+const char *AclMatchedName = nullptr;
 
-const char *AclMatchedName = NULL;
+namespace Acl {
 
-bool ACLFlags::supported(const ACLFlag f) const
+/// ACL type name comparison functor
+class TypeNameCmp {
+public:
+    bool operator()(TypeName a, TypeName b) const { return strcmp(a, b) < 0; }
+};
+
+/// ACL makers indexed by ACL type name
+typedef std::map<TypeName, Maker, TypeNameCmp> Makers;
+
+/// registered ACL Makers
+static Makers &
+TheMakers()
 {
-    if (f == ACL_F_REGEX_CASE)
-        return true;
-    return (supported_.find(f) != std::string::npos);
+    static Makers Registry;
+    return Registry;
 }
 
-void
-ACLFlags::parseFlags()
+/// creates an ACL object of the named (and already registered) ACL child type
+static
+ACL *
+Make(TypeName typeName)
 {
-    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)
-            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();
-            }
-        }
+    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
     }
 
-    /*Regex code needs to parse -i file*/
-    if ( isSet(ACL_F_REGEX_CASE)) {
-        ConfigParser::TokenPutBack("-i");
-        makeUnSet('i');
-    }
+    ACL *result = (pos->second)(pos->first);
+    debugs(28, 4, typeName << '=' << result);
+    assert(result);
+    return result;
 }
 
-const char *
-ACLFlags::flagsStr() const
+} // namespace Acl
+
+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 *
@@ -105,23 +100,12 @@ ACL::FindByName(const char *name)
 
     debugs(28, 9, "ACL::FindByName found no match");
 
-    return NULL;
-}
-
-ACL *
-ACL::Factory (char const *type)
-{
-    ACL *result = Prototype::Factory (type);
-
-    if (!result)
-        fatal ("Unknown acl type in ACL::Factory");
-
-    return result;
+    return nullptr;
 }
 
 ACL::ACL() :
-    cfgline(NULL),
-    next(NULL),
+    cfgline(nullptr),
+    next(nullptr),
     registered(false)
 {
     *name = 0;
@@ -135,7 +119,6 @@ bool ACL::valid () const
 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
@@ -144,20 +127,26 @@ ACL::matches(ACLChecklist *checklist) const
     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->verifyAle();
+
         // 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
 }
 
@@ -176,15 +165,15 @@ void
 ACL::ParseAclLine(ConfigParser &parser, ACL ** head)
 {
     /* we're already using strtok() to grok the line */
-    char *t = NULL;
-    ACL *A = NULL;
+    char *t = nullptr;
+    ACL *A = nullptr;
     LOCAL_ARRAY(char, aclname, ACL_NAME_SZ);
     int new_acl = 0;
 
     /* snarf the ACL name */
 
-    if ((t = ConfigParser::NextToken()) == NULL) {
-        debugs(28, DBG_CRITICAL, "aclParseAclLine: missing ACL name.");
+    if ((t = ConfigParser::NextToken()) == nullptr) {
+        debugs(28, DBG_CRITICAL, "ERROR: aclParseAclLine: missing ACL name.");
         parser.destruct();
         return;
     }
@@ -200,8 +189,8 @@ ACL::ParseAclLine(ConfigParser &parser, ACL ** head)
     /* snarf the ACL type */
     const char *theType;
 
-    if ((theType = ConfigParser::NextToken()) == NULL) {
-        debugs(28, DBG_CRITICAL, "aclParseAclLine: missing ACL type.");
+    if ((theType = ConfigParser::NextToken()) == nullptr) {
+        debugs(28, DBG_CRITICAL, "ERROR: aclParseAclLine: missing ACL type.");
         parser.destruct();
         return;
     }
@@ -209,17 +198,17 @@ ACL::ParseAclLine(ConfigParser &parser, ACL ** head)
     // Is this ACL going to work?
     if (strcmp(theType, "myip") == 0) {
         AnyP::PortCfgPointer p = HttpPortList;
-        while (p != NULL) {
+        while (p != nullptr) {
             // Bug 3239: not reliable when there is interception traffic coming
             if (p->flags.natIntercept)
                 debugs(28, DBG_CRITICAL, "WARNING: 'myip' ACL is not reliable for interception proxies. Please use 'myportname' instead.");
             p = p->next;
         }
-        debugs(28, DBG_IMPORTANT, "UPGRADE: ACL 'myip' type is has been renamed to 'localip' and matches the IP the client connected to.");
+        debugs(28, DBG_IMPORTANT, "WARNING: UPGRADE: ACL 'myip' type has been renamed to 'localip' and matches the IP the client connected to.");
         theType = "localip";
     } else if (strcmp(theType, "myport") == 0) {
         AnyP::PortCfgPointer p = HttpPortList;
-        while (p != NULL) {
+        while (p != nullptr) {
             // Bug 3239: not reliable when there is interception traffic coming
             // Bug 3239: myport - not reliable (yet) when there is interception traffic coming
             if (p->flags.natIntercept)
@@ -227,19 +216,19 @@ ACL::ParseAclLine(ConfigParser &parser, ACL ** head)
             p = p->next;
         }
         theType = "localport";
-        debugs(28, DBG_IMPORTANT, "UPGRADE: ACL 'myport' type is has been renamed to 'localport' and matches the port the client connected to.");
-    }
-
-    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;
+        debugs(28, DBG_IMPORTANT, "WARNING: UPGRADE: ACL 'myport' type 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), "WARNING: UPGRADE: ACL 'manager' is now a built-in ACL. Remove it from your config file.");
+        return; // ignore the line
+    } else if (strcmp(theType, "clientside_mark") == 0) {
+        debugs(28, DBG_IMPORTANT, "WARNING: UPGRADE: ACL 'clientside_mark' type has been renamed to 'client_connection_mark'.");
+        theType = "client_connection_mark";
     }
 
-    if ((A = FindByName(aclname)) == NULL) {
+    if ((A = FindByName(aclname)) == nullptr) {
         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 {
@@ -259,7 +248,7 @@ ACL::ParseAclLine(ConfigParser &parser, ACL ** head)
      */
     AclMatchedName = A->name;   /* ugly */
 
-    A->flags.parseFlags();
+    A->parseFlags();
 
     /*split the function here */
     A->parse();
@@ -267,13 +256,13 @@ ACL::ParseAclLine(ConfigParser &parser, ACL ** head)
     /*
      * Clear AclMatchedName from our temporary hack
      */
-    AclMatchedName = NULL;  /* ugly */
+    AclMatchedName = nullptr;  /* ugly */
 
     if (!new_acl)
         return;
 
     if (A->empty()) {
-        debugs(28, DBG_CRITICAL, "Warning: empty ACL: " << A->cfgline);
+        debugs(28, DBG_CRITICAL, "WARNING: empty ACL: " << A->cfgline);
     }
 
     if (!A->valid()) {
@@ -296,6 +285,38 @@ ACL::isProxyAuth() const
     return false;
 }
 
+void
+ACL::parseFlags()
+{
+    Acl::Options allOptions = options();
+    for (const auto lineOption: lineOptions()) {
+        lineOption->unconfigure(); // forget any previous "acl ..." line effects
+        allOptions.push_back(lineOption);
+    }
+    Acl::ParseFlags(allOptions);
+}
+
+SBufList
+ACL::dumpOptions()
+{
+    SBufList result;
+
+    const auto &myOptions = options();
+    // XXX: No lineOptions() call here because we do not remember ACL "line"
+    // boundaries and associated "line" options; we cannot report them.
+
+    // 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
@@ -334,9 +355,7 @@ ACL::cacheMatchAcl(dlink_list * cache, ACLChecklist *checklist)
         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;
@@ -360,6 +379,12 @@ aclCacheMatchFlush(dlink_list * cache)
     }
 }
 
+bool
+ACL::requiresAle() const
+{
+    return false;
+}
+
 bool
 ACL::requiresReply() const
 {
@@ -380,70 +405,7 @@ ACL::~ACL()
 {
     debugs(28, 3, "freeing ACL " << name);
     safe_free(cfgline);
-    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;
+    AclMatchedName = nullptr; // in case it was pointing to our name
 }
 
 void