Patch Description
===================

This patch adds support for two new ACL types: one-of and all-of (as
agreed on the "or ACLs" squid-dev thread). Each new ACL type will take a
list of optionally negated ACL names as parameters. All ACL name
parameters must be already defined.

    acl AorB one-of a b
    acl CorNotD one-of c !b
    acl EandNeitherAnorB all-of e !AorB

This is a Measurement Factory project


Comments
=========

Unfortunately this is a patch we are not happy with. Alex have many
concerns, but I believe that it is not so bad.

In the followings I am trying to explain the patch.

New acls interpretation
------------------------

The patch is implemented as follows:
 - The new acls converted to Checklists in order to computed.
 - The "any-of" acl converted as follows:
    acl any-of acl1 acl2 acl3
   To:
    Checklist allow acl1
    Checklist allow acl2
    Checklist allow acl3
    Checklist deny all

 - The "all-of" acl converted as follows.
    acl "all-of" acl1 acl2 acl3
   To:
    Checklist allow acl1 acl2 acl3
    Checklist deny all

The above looks trivial to implement it but there are problems when
someone tries to develop it, mostly because of the current acls check
scheme used in squid.

Squid acls checking
--------------------

This is what we are doing currently in squid. Assume the following
access list:
   checklist allow acl11 acl12 acl13 acl14
   checklist allow acl21 acl22 acl23
   ....

When we are checking the checklist we are doing the following:
  1) Do a fast check for the acl11, acl12 etc
  2) Assume that the acl13 requires an async lookup. We are scheduling
the lookup. The fast acl check fails.
  3) After the async lookup for the acl13 finished:
    - we are checking again all the access line.
    - when the acl13 checked for second time and depending the acl we
are getting the value with a fast lookup from a cached value in
checklist object or from an other cache.


What this patch  does
-----------------------

Assume that the acl13 acl in the above example is an "one-of" or an
"any-of" acl.
When we are checking the acl13:
   a) create the acl13::checklist which interprets the acl13 acl
   b) copy all cached values from current checklist to acl13::checklist
   c) To a fast lookup to acl13::checklist.
   d) if it fails schedule slow checklist lookup for the acl13::checklist


Comments,concerns etc
----------------------

In the above example when the async lookup for the acl13 finishes do not
start the checklist from the beginning of the current line, but start
the check from the acl13 and continue with the next acls.

This is (and depending the implementation) will help us to implement  a
little better "any-of" and "all-of" acls.

My question is, is this any reason why squid is checking from the
beginning the access line when an acl needs lookup?
Is n't it add a performance overhead?

Also for example for the source domain acl, we are getting the domain
always from the fqdncache cache. Is n't it a little slow? What if the
value did not found even in the second lookup, for example because
expired in cache, or it is replaced by other value?


Regards,
    Christos
=== modified file 'src/AclRegs.cc'
--- src/AclRegs.cc	2012-10-29 01:31:29 +0000
+++ src/AclRegs.cc	2012-11-21 11:50:52 +0000
@@ -1,59 +1,62 @@
 #include "squid.h"
 
 /** This file exists to provide satic registration code to executables
     that need ACLs. We cannot place this code in acl/lib*.la because it
     does not get linked in, because nobody is using these classes by name.
 */
 
 #include "acl/Acl.h"
+#include "acl/AllOfData.h"
 #if USE_SQUID_EUI
 #include "acl/Arp.h"
 #include "acl/Eui64.h"
 #endif
 #include "acl/Asn.h"
 #include "acl/Browser.h"
 #include "acl/Checklist.h"
 #include "acl/Data.h"
 #include "acl/DestinationAsn.h"
 #include "acl/DestinationDomain.h"
 #include "acl/DestinationIp.h"
 #include "acl/DomainData.h"
 #if USE_AUTH
 #include "acl/ExtUser.h"
 #endif
 #include "acl/FilledChecklist.h"
 #include "acl/Gadgets.h"
+#include "acl/Group.h"
 #include "acl/HierCodeData.h"
 #include "acl/HierCode.h"
 #include "acl/HttpHeaderData.h"
 #include "acl/HttpRepHeader.h"
 #include "acl/HttpReqHeader.h"
 #include "acl/HttpStatus.h"
 #include "acl/IntRange.h"
 #include "acl/Ip.h"
 #include "acl/LocalIp.h"
 #include "acl/LocalPort.h"
 #include "acl/MaxConnection.h"
 #include "acl/MethodData.h"
 #include "acl/Method.h"
 #include "acl/MyPortName.h"
+#include "acl/OneOfData.h"
 #include "acl/PeerName.h"
 #include "acl/ProtocolData.h"
 #include "acl/Protocol.h"
 #include "acl/Random.h"
 #include "acl/Referer.h"
 #include "acl/RegexData.h"
 #include "acl/ReplyHeaderStrategy.h"
 #include "acl/ReplyMimeType.h"
 #include "acl/RequestHeaderStrategy.h"
 #include "acl/RequestMimeType.h"
 #include "acl/SourceAsn.h"
 #include "acl/SourceDomain.h"
 #include "acl/SourceIp.h"
 #if USE_SSL
 #include "acl/SslErrorData.h"
 #include "acl/SslError.h"
 #include "acl/CertificateData.h"
 #include "acl/Certificate.h"
 #endif
 #include "acl/Strategised.h"
@@ -157,20 +160,26 @@
 
 #if USE_IDENT
 ACL::Prototype ACLIdent::UserRegistryProtoype(&ACLIdent::UserRegistryEntry_, "ident");
 ACLIdent ACLIdent::UserRegistryEntry_(new ACLUserData, "ident");
 ACL::Prototype ACLIdent::RegexRegistryProtoype(&ACLIdent::RegexRegistryEntry_, "ident_regex" );
 ACLIdent ACLIdent::RegexRegistryEntry_(new ACLRegexData, "ident_regex");
 #endif
 
 #if USE_AUTH
 ACL::Prototype ACLProxyAuth::UserRegistryProtoype(&ACLProxyAuth::UserRegistryEntry_, "proxy_auth");
 ACLProxyAuth ACLProxyAuth::UserRegistryEntry_(new ACLUserData, "proxy_auth");
 ACL::Prototype ACLProxyAuth::RegexRegistryProtoype(&ACLProxyAuth::RegexRegistryEntry_, "proxy_auth_regex" );
 ACLProxyAuth ACLProxyAuth::RegexRegistryEntry_(new ACLRegexData, "proxy_auth_regex");
 
 ACL::Prototype ACLMaxUserIP::RegistryProtoype(&ACLMaxUserIP::RegistryEntry_, "max_user_ip");
 ACLMaxUserIP ACLMaxUserIP::RegistryEntry_("max_user_ip");
 #endif
 
 ACL::Prototype ACLTag::RegistryProtoype(&ACLTag::RegistryEntry_, "tag");
 ACLStrategised<const char *> ACLTag::RegistryEntry_(new ACLStringData, ACLTagStrategy::Instance(), "tag");
+
+ACL::Prototype ACLGroup::OrRegistryProtoype(&ACLGroup::OrRegistryEntry_, "one-of");
+ACLStrategised<ACLFilledChecklist *> ACLGroup::OrRegistryEntry_(new ACLOneOfData, ACLGroupStrategy::Instance(), "one-of");
+
+ACL::Prototype ACLGroup::AndRegistryProtoype(&ACLGroup::AndRegistryEntry_, "all-of");
+ACLStrategised<ACLFilledChecklist *> ACLGroup::AndRegistryEntry_(new ACLAllOfData, ACLGroupStrategy::Instance(), "all-of");

=== added file 'src/acl/AllOfData.cc'
--- src/acl/AllOfData.cc	1970-01-01 00:00:00 +0000
+++ src/acl/AllOfData.cc	2012-11-21 11:48:03 +0000
@@ -0,0 +1,55 @@
+/*
+ */
+
+#include "squid.h"
+#include "acl/Checklist.h"
+#include "acl/AllOfData.h"
+#include "ConfigParser.h"
+#include "cache_cf.h"
+#include "Debug.h"
+#include "Gadgets.h"
+#include "wordlist.h"
+
+ACLAllOfData::~ACLAllOfData()
+{
+}
+
+wordlist *
+ACLAllOfData::dump()
+{
+    if (!accessList)
+        return NULL;
+
+    wordlist *W = NULL;
+    for (ACLList *l = accessList->aclList; l != NULL; l = l->next)
+        wordlistAdd(&W, l->_acl->name);
+
+    return W;
+}
+
+void
+ACLAllOfData::parse()
+{
+    ConfigParser parser;
+    assert(accessList == NULL);
+    acl_access **tail = &accessList;
+
+    // The AND acl:
+    // acl AND acl1 acl2 acl3 ...
+    // Implemented as the following checklist
+    // checklist allow acl1 acl2 acl3 ...
+    // checklist deny all
+
+    acl_access *A = new acl_access;
+    A->allow = ACCESS_ALLOWED;
+    aclParseAclList(parser, &A->aclList);
+    *tail = A;
+    tail = &A->next;
+}
+
+ACLData<ACLFilledChecklist *> *
+ACLAllOfData::clone() const
+{
+    assert (!accessList);
+    return new ACLAllOfData;
+}

=== added file 'src/acl/AllOfData.h'
--- src/acl/AllOfData.h	1970-01-01 00:00:00 +0000
+++ src/acl/AllOfData.h	2012-11-22 14:23:48 +0000
@@ -0,0 +1,31 @@
+/*
+ */
+
+#ifndef SQUID_ACLALLOFDATA_H
+#define SQUID_ACLALLOFDATA_H
+
+#include "splay.h"
+#include "acl/Acl.h"
+#include "acl/Data.h"
+#include "acl/FilledChecklist.h"
+#include "acl/Group.h"
+
+/**
+ * \ingroup ACLAPI
+ * all-of ACL data
+ */
+class ACLAllOfData : public ACLGroupData
+{
+public:
+    MEMPROXY_CLASS(ACLAllOfData);
+    virtual ~ACLAllOfData();
+
+    // ACLData API
+    virtual wordlist *dump();
+    virtual void parse();
+    virtual ACLData<ACLFilledChecklist *> *clone() const;
+};
+
+MEMPROXY_CLASS_INLINE(ACLAllOfData);
+
+#endif /* SQUID_ACLALLOFDATA_H */

=== modified file 'src/acl/Checklist.cc'
--- src/acl/Checklist.cc	2012-09-01 14:38:36 +0000
+++ src/acl/Checklist.cc	2012-11-21 09:49:38 +0000
@@ -56,46 +56,50 @@
         return;
     }
 
     allow_t lastSeenKeyword = ACCESS_DUNNO;
     /* NOTE: This holds a cbdata reference to the current access_list
      * entry, not the whole list.
      */
     while (accessList != NULL) {
         /** \par
          * If the _acl_access is no longer valid (i.e. its been
          * freed because of a reconfigure), then bail with ACCESS_DUNNO.
          */
 
         if (!cbdataReferenceValid(accessList)) {
             cbdataReferenceDone(accessList);
             debugs(28, 4, "ACLChecklist::check: " << this << " accessList is invalid");
             checkCallback(ACCESS_DUNNO);
             return;
         }
 
-        checking (true);
-        checkAccessList();
-        checking (false);
-
-        if (asyncInProgress()) {
-            return;
+        // We may call matchNonBlocking to complete acl non-blocking checking
+        // and call the callbacks. In this case do not call checkAccessList.
+        if (!finished()) {
+            checking (true);
+            checkAccessList();
+            checking (false);
+
+            if (asyncInProgress()) {
+                return;
+            }
         }
 
         if (finished()) {
             /** \par
              * Either the request is allowed, denied, requires authentication.
              */
             debugs(28, 3, "ACLChecklist::check: " << this << " match found, 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;
 

=== modified file 'src/acl/FilledChecklist.cc'
--- src/acl/FilledChecklist.cc	2012-09-09 19:41:47 +0000
+++ src/acl/FilledChecklist.cc	2012-12-05 17:23:47 +0000
@@ -177,20 +177,54 @@
     if (http_request != NULL) {
         request = HTTPMSGLOCK(http_request);
 #if FOLLOW_X_FORWARDED_FOR
         if (Config.onoff.acl_uses_indirect_client)
             src_addr = request->indirect_client_addr;
         else
 #endif /* FOLLOW_X_FORWARDED_FOR */
             src_addr = request->client_addr;
         my_addr = request->my_addr;
 
         if (request->clientConnectionManager.valid())
             conn(request->clientConnectionManager.get());
     }
 
 #if USE_IDENT
     if (ident)
         xstrncpy(rfc931, ident, USER_IDENT_SZ);
 #endif
 }
 
+ACLFilledChecklist *ACLFilledChecklist::cloneFilled()
+{
+    ACLFilledChecklist *newFilled = new ACLFilledChecklist;
+    newFilled->src_addr = src_addr;
+    newFilled->dst_addr = dst_addr;
+    newFilled->my_addr = my_addr;
+    newFilled->dst_peer = dst_peer;
+    newFilled->dst_rdns = dst_rdns ? xstrdup(dst_rdns) : NULL;
+
+    if (request)
+        newFilled->request = HTTPMSGLOCK(request);
+    else 
+        newFilled->request = NULL;
+    if (reply)
+        newFilled->reply = HTTPMSGLOCK(reply);
+    else
+        newFilled->reply = NULL;
+    xstrncpy(newFilled->rfc931, rfc931, USER_IDENT_SZ);
+#if USE_AUTH
+    newFilled->auth_user_request = auth_user_request;
+#endif
+#if SQUID_SNMP
+    newFilled->snmp_community = snmp_community;
+#endif
+#if USE_SSL
+    newFilled->sslErrors = cbdataReference(sslErrors);
+#endif
+    newFilled->extacl_entry = cbdataReference(extacl_entry);
+    newFilled->conn_ = cbdataReference(conn_);
+    newFilled->fd_ = fd_;
+    newFilled->destinationDomainChecked_ = destinationDomainChecked_;
+    newFilled->sourceDomainChecked_ = sourceDomainChecked_;
+    return newFilled;
+}

=== modified file 'src/acl/FilledChecklist.h'
--- src/acl/FilledChecklist.h	2012-10-03 07:34:10 +0000
+++ src/acl/FilledChecklist.h	2012-12-05 17:20:45 +0000
@@ -34,40 +34,41 @@
     ConnStateData * conn() const;
 
     /// The client side fd. It uses conn() if available
     int fd() const;
 
     /// set either conn
     void conn(ConnStateData *);
     /// set the client side FD
     void fd(int aDescriptor);
 
     //int authenticated();
 
     bool destinationDomainChecked() const;
     void markDestinationDomainChecked();
     bool sourceDomainChecked() const;
     void markSourceDomainChecked();
 
     // ACLChecklist API
     virtual bool hasRequest() const { return request != NULL; }
     virtual bool hasReply() const { return reply != NULL; }
+    ACLFilledChecklist *cloneFilled();
 
 public:
     Ip::Address src_addr;
     Ip::Address dst_addr;
     Ip::Address my_addr;
     CachePeer *dst_peer;
     char *dst_rdns;
 
     HttpRequest *request;
     HttpReply *reply;
 
     char rfc931[USER_IDENT_SZ];
 #if USE_AUTH
     Auth::UserRequest::Pointer auth_user_request;
 #endif
 #if SQUID_SNMP
     char *snmp_community;
 #endif
 
 #if USE_SSL

=== added file 'src/acl/Group.cc'
--- src/acl/Group.cc	1970-01-01 00:00:00 +0000
+++ src/acl/Group.cc	2012-12-05 19:26:53 +0000
@@ -0,0 +1,121 @@
+/*
+ */
+
+#include "squid.h"
+#include "acl/Checklist.h"
+#include "acl/Gadgets.h"
+#include "acl/Group.h"
+#include "fqdncache.h"
+#include "HttpRequest.h"
+#include "HttpReply.h"
+#include "ipcache.h"
+
+ACLGroupStrategy ACLGroupStrategy::Instance_;
+
+CBDATA_NAMESPACED_CLASS_INIT(ACLGroupStrategy,GroupStrategyCBData);
+
+class GroupLookup : public ACLChecklist::AsyncState
+{
+public:
+    static GroupLookup *Instance();
+    virtual void checkForAsync(ACLChecklist *)const;
+private:
+    static GroupLookup instance_;
+};
+
+GroupLookup GroupLookup::instance_;
+
+GroupLookup *
+GroupLookup::Instance()
+{
+    return &instance_;
+}
+
+void
+GroupLookup::checkForAsync(ACLChecklist *cl) const
+{
+    ACLFilledChecklist *checklist = Filled(cl);
+    checklist->asyncInProgress(true);
+}
+
+ACLGroupData::~ACLGroupData()
+{
+    aclDestroyAccessList(&accessList);
+}
+
+ACLGroupStrategy::GroupStrategyCBData::GroupStrategyCBData(ACLFilledChecklist *group, ACLFilledChecklist *current)
+{
+    groupCheck = cbdataReference(group);
+    currentCheck = cbdataReference(current);
+}
+
+ACLGroupStrategy::GroupStrategyCBData::~GroupStrategyCBData()
+{
+    cbdataReferenceDone(groupCheck);
+    cbdataReferenceDone(currentCheck);
+}
+
+
+void
+ACLGroupStrategy::GroupStrategyCB(allow_t answer, void *data)
+{
+    ACLGroupStrategy::GroupStrategyCBData *groupData = (ACLGroupStrategy::GroupStrategyCBData *)data;
+    ACLFilledChecklist *groupCheck = groupData->groupCheck;
+    ACLFilledChecklist *checklist = groupData->currentCheck;
+
+    //Copy looked up values
+    if (!checklist->destinationDomainChecked() && groupCheck->destinationDomainChecked()) {
+        if (!checklist->dst_rdns && groupCheck->dst_rdns)
+            checklist->dst_rdns = xstrdup(groupCheck->dst_rdns);
+        checklist->dst_addr = groupCheck->dst_addr;
+        checklist->markDestinationDomainChecked();
+    }
+
+    if (!checklist->sourceDomainChecked() && groupCheck->sourceDomainChecked())
+        checklist->markSourceDomainChecked();
+
+    if (checklist->extacl_entry != groupCheck->extacl_entry) {
+        cbdataReferenceDone(checklist->extacl_entry);
+        checklist->extacl_entry = cbdataReference(groupCheck->extacl_entry);
+    }
+
+    debugs(28, 5, HERE << "Slow acl check finished. The answer is: " << answer);
+    checklist->asyncInProgress(false);
+    checklist->changeState (ACLChecklist::NullState::Instance());
+    checklist->matchNonBlocking();
+    delete groupData;
+}
+
+int
+ACLGroupStrategy::match (ACLData<MatchType> * &data, ACLFilledChecklist *checklist)
+{
+    ACLGroupData *chData = dynamic_cast<ACLGroupData *>(data);
+    ACLFilledChecklist *groupCheck = checklist->cloneFilled();
+    groupCheck->accessList = cbdataReference(chData->accessList);
+
+    if (checklist->reply)
+        groupCheck->reply = HTTPMSGLOCK(checklist->reply);
+    
+    allow_t answer;
+    if ((answer = groupCheck->fastCheck()) == ACCESS_DUNNO) {
+        checklist->changeState(GroupLookup::Instance());
+        ACLGroupStrategy::GroupStrategyCBData *groupCbData = new ACLGroupStrategy::GroupStrategyCBData(groupCheck, checklist);
+        assert(cbdataReferenceValid(groupCheck));
+        groupCheck->nonBlockingCheck(GroupStrategyCB, groupCbData);
+        return 0;
+    } else
+       return answer == ACCESS_ALLOWED;
+}
+
+ACLGroupStrategy *
+ACLGroupStrategy::Instance()
+{
+    return &Instance_;
+}
+
+bool
+ACLGroupData::match(ACLFilledChecklist *checklist)
+{
+    assert(false);
+    return false;
+}

=== added file 'src/acl/Group.h'
--- src/acl/Group.h	1970-01-01 00:00:00 +0000
+++ src/acl/Group.h	2012-12-05 18:52:05 +0000
@@ -0,0 +1,87 @@
+/*
+ */
+
+#ifndef SQUID_ACLSYNTHESIZED_H
+#define SQUID_ACLSYNTHESIZED_H
+
+#include "acl/Acl.h"
+#include "acl/Data.h"
+#include "acl/Checklist.h"
+#include "acl/Strategised.h"
+
+/**
+ * \ingroup ACLAPI
+ * Applies ACLData API to a group of ACLs
+ */
+class ACLGroupData : public ACLData<ACLFilledChecklist *>
+{
+
+public:
+    virtual ~ACLGroupData();
+    
+    // ACLData API
+    virtual bool match(ACLFilledChecklist *checklist);
+    virtual wordlist *dump() =0;
+    virtual void parse() =0;
+    virtual bool empty() const {return !accessList; }
+    virtual ACLData<ACLFilledChecklist *> *clone() const =0;
+
+    /// individual ACLs storage with the right AND/OR/NOT rules structure
+    acl_access *accessList;
+};
+
+/**
+ * \ingroup ACLAPI
+ * Applies ACLStrategy API to a group of ACLs
+ */
+class ACLGroupStrategy : public ACLStrategy<ACLFilledChecklist *>
+{
+public:
+    ///Cbdata class used to pass data to the GroupStrategyCB callback function
+    class GroupStrategyCBData {
+    public:
+        ACLFilledChecklist *groupCheck;
+        ACLFilledChecklist *currentCheck;
+        GroupStrategyCBData(ACLFilledChecklist *groupCheck, ACLFilledChecklist *currentCheck);
+        ~GroupStrategyCBData();
+        CBDATA_CLASS2(GroupStrategyCBData);
+    };
+
+public:
+    static ACLGroupStrategy *Instance();
+
+    // ACLStrategy API
+    virtual int match (ACLData<MatchType> * &, ACLFilledChecklist *);
+    virtual bool requiresRequest() const {return true;}
+
+    /**
+     * Not implemented to prevent copies of the instance.
+     \par
+     * Not private to prevent brain dead g++ warnings about
+     * private constructors with no friends
+     */
+    ACLGroupStrategy(ACLGroupStrategy const &);
+
+private:
+    /// ACLChecklist callback called for nonblocking ACLs group
+    static void GroupStrategyCB(allow_t answer, void *data);
+
+    ACLGroupStrategy() {}
+    /// Not implemented
+    ACLGroupStrategy &operator=(ACLGroupStrategy const &);
+
+    static ACLGroupStrategy Instance_;
+};
+
+/// \ingroup ACLAPI
+class ACLGroup
+{
+
+private:
+    static ACL::Prototype OrRegistryProtoype;
+    static ACL::Prototype AndRegistryProtoype;
+    static ACLStrategised<ACLFilledChecklist *> OrRegistryEntry_;
+    static ACLStrategised<ACLFilledChecklist *> AndRegistryEntry_;
+};
+
+#endif /* SQUID_ACLSYNTHESIZED_H */

=== modified file 'src/acl/Makefile.am'
--- src/acl/Makefile.am	2012-10-29 01:31:29 +0000
+++ src/acl/Makefile.am	2012-11-21 11:50:52 +0000
@@ -19,98 +19,104 @@
 	Strategised.h \
 	FilledChecklist.cc \
 	FilledChecklist.h \
 	AclAddress.h \
 	AclAddress.cc
 
 ## data-specific ACLs
 libacls_la_SOURCES = \
 	IntRange.cc \
 	IntRange.h \
 	RegexData.cc \
 	RegexData.h \
 	StringData.cc \
 	StringData.h \
 	Time.cc \
 	Time.h \
 	TimeData.cc \
 	TimeData.h \
 	Asn.cc \
 	Asn.h \
+	AllOfData.cc \
+	AllOfData.h \
 	Browser.cc \
 	Browser.h \
 	DestinationAsn.h \
 	DestinationDomain.cc \
 	DestinationDomain.h \
 	DestinationIp.cc \
 	DestinationIp.h \
 	DomainData.cc \
 	DomainData.h \
 	ExtUser.cc \
 	ExtUser.h \
 	HierCodeData.cc \
 	HierCodeData.h \
 	HierCode.cc \
 	HierCode.h \
 	HttpHeaderData.cc \
 	HttpHeaderData.h \
 	HttpRepHeader.cc \
 	HttpRepHeader.h \
 	HttpReqHeader.cc \
 	HttpReqHeader.h \
 	HttpStatus.cc \
 	HttpStatus.h \
 	Ip.cc \
 	Ip.h \
 	LocalIp.cc \
 	LocalIp.h \
 	LocalPort.cc \
 	LocalPort.h \
 	MaxConnection.cc \
 	MaxConnection.h \
 	Method.cc \
 	MethodData.cc \
 	MethodData.h \
 	Method.h \
 	MyPortName.cc \
 	MyPortName.h \
+	OneOfData.cc \
+	OneOfData.h \
 	PeerName.cc \
 	PeerName.h \
 	Protocol.cc \
 	ProtocolData.cc \
 	ProtocolData.h \
 	Protocol.h \
 	Random.cc \
 	Random.h \
 	Referer.cc \
 	Referer.h \
 	ReplyHeaderStrategy.h \
 	ReplyMimeType.cc \
 	ReplyMimeType.h \
 	RequestHeaderStrategy.h \
 	RequestMimeType.cc \
 	RequestMimeType.h \
 	SourceAsn.h \
 	SourceDomain.cc \
 	SourceDomain.h \
 	SourceIp.cc \
 	SourceIp.h \
+	Group.cc \
+	Group.h \
 	Tag.cc \
 	Tag.h \
 	Url.cc \
 	Url.h \
 	UrlLogin.cc \
 	UrlLogin.h \
 	UrlPath.cc \
 	UrlPath.h \
 	UrlPort.cc \
 	UrlPort.h \
 	UserData.cc \
 	UserData.h \
 	AclNameList.h \
 	AclDenyInfoList.h \
 	Gadgets.cc \
 	Gadgets.h \
 	AclSizeLimit.h
 
 ## Add conditional sources
 ## TODO: move these to their respectful dirs when those dirs are created

=== added file 'src/acl/OneOfData.cc'
--- src/acl/OneOfData.cc	1970-01-01 00:00:00 +0000
+++ src/acl/OneOfData.cc	2012-11-21 11:48:19 +0000
@@ -0,0 +1,80 @@
+/*
+ */
+
+#include "squid.h"
+#include "acl/Checklist.h"
+#include "acl/OneOfData.h"
+#include "ConfigParser.h"
+#include "cache_cf.h"
+#include "Debug.h"
+#include "Gadgets.h"
+#include "wordlist.h"
+
+ACLOneOfData::~ACLOneOfData()
+{
+}
+
+wordlist *
+ACLOneOfData::dump()
+{
+    if (!accessList)
+        return NULL;
+
+    wordlist *W = NULL;
+    for (acl_access *l = accessList; l->allow != ACCESS_DENIED; l = l->next) {
+        assert(l->aclList && l->aclList->_acl);
+        wordlistAdd(&W, l->aclList->_acl->name);
+    }
+
+    return W;
+}
+
+void
+ACLOneOfData::parse()
+{
+    char *t = NULL;
+
+    // The OR acl:
+    // acl OR acl1 acl2 acl3 ...
+    // Implemented as the following checklist
+    // checklist allow acl1
+    // checklist allow acl2
+    // checklist allow acl3
+    // ......
+    // checklist deny all
+
+    assert(accessList == NULL);
+    acl_access **tail = &accessList;
+    ACL *a = NULL;
+    while ((t = strtokFile())) {
+        ACLList *L = new ACLList;
+
+        if (*t == '!') {
+            L->negated (true);
+            ++t;
+        }
+ 
+        a = ACL::FindByName(t);
+
+        if (a == NULL) {
+            debugs(28, DBG_CRITICAL, HERE << " ACL name '" << t << "' not found.");
+            delete L;
+            self_destruct();
+            return;
+        }
+
+        L->_acl = a;
+        acl_access *A = new acl_access;
+        A->allow = ACCESS_ALLOWED;
+        A->aclList = L;
+        *tail = A;
+        tail = &A->next;
+    }
+}
+
+ACLData<ACLFilledChecklist *> *
+ACLOneOfData::clone() const
+{
+    assert (!accessList);
+    return new ACLOneOfData;
+}

=== added file 'src/acl/OneOfData.h'
--- src/acl/OneOfData.h	1970-01-01 00:00:00 +0000
+++ src/acl/OneOfData.h	2012-11-22 14:23:22 +0000
@@ -0,0 +1,31 @@
+/*
+ */
+
+#ifndef SQUID_ACLONEOFDATA_H
+#define SQUID_ACLONEOFDATA_H
+
+#include "splay.h"
+#include "acl/Acl.h"
+#include "acl/Data.h"
+#include "acl/FilledChecklist.h"
+#include "acl/Group.h"
+
+/**
+ * \ingroup ACLAPI
+ * one-of ACL data
+ */
+class ACLOneOfData : public ACLGroupData
+{
+public:
+    MEMPROXY_CLASS(ACLOneOfData);
+    virtual ~ACLOneOfData();
+
+    /* ACLData API */ 
+    virtual wordlist *dump();
+    virtual void parse();
+    virtual ACLData<ACLFilledChecklist *> *clone() const;
+};
+
+MEMPROXY_CLASS_INLINE(ACLOneOfData);
+
+#endif /* SQUID_ACLONEOFDATA_H */

=== modified file 'src/cf.data.pre'
--- src/cf.data.pre	2012-10-29 01:31:29 +0000
+++ src/cf.data.pre	2012-11-22 14:25:16 +0000
@@ -896,40 +896,49 @@
 	  # match against SSL certificate validation error [fast]
 	  #
 	  # For valid error names see in @DEFAULT_ERROR_DIR@/templates/error-details.txt
 	  # template file.
 	  #
 	  # The following can be used as shortcuts for certificate properties:
 	  #  [ssl::]certHasExpired: the "not after" field is in the past
 	  #  [ssl::]certNotYetValid: the "not before" field is in the future
 	  #  [ssl::]certUntrusted: The certificate issuer is not to be trusted.
 	  #  [ssl::]certSelfSigned: The certificate is self signed.
 	  #  [ssl::]certDomainMismatch: The certificate CN domain does not
 	  #         match the name the name of the host we are connecting to.
 	  #
 	  # The ssl::certHasExpired, ssl::certNotYetValid, ssl::certDomainMismatch,
 	  # ssl::certUntrusted, and ssl::certSelfSigned can also be used as
 	  # predefined ACLs, just like the 'all' ACL.
 	  #
 	  # NOTE: The ssl_error ACL is only supported with sslproxy_cert_error,
 	  # sslproxy_cert_sign, and sslproxy_cert_adapt options.
 ENDIF
+	acl aclname one-of acl1 acl2 ...
+	  # match any one of the acls [fast or slow]
+	  # The first matching ACL stops further ACL evaluation.
+	  # The one-of ACL is fast if all ACLs 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.
+	  # The all-of ACL is fast if all ACLs are fast and slow otherwise.
 
 	Examples:
 		acl macaddress arp 09:00:2b:23:45:67
 		acl myexample dst_as 1241
 		acl password proxy_auth REQUIRED
 		acl fileupload req_mime_type -i ^multipart/form-data$
 		acl javascript rep_mime_type -i ^application/x-javascript$
 
 NOCOMMENT_START
 #
 # Recommended minimum configuration:
 #
 
 # Example rule allowing access from your local networks.
 # Adapt to list your (internal) IP networks from where browsing
 # should be allowed
 acl localnet src 10.0.0.0/8	# RFC1918 possible internal network
 acl localnet src 172.16.0.0/12	# RFC1918 possible internal network
 acl localnet src 192.168.0.0/16	# RFC1918 possible internal network
 acl localnet src fc00::/7       # RFC 4193 local private network range

Reply via email to