]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Extracted transaction state storage and related checks from ACLChecklist into
authorAlex Rousskov <rousskov@measurement-factory.com>
Sun, 8 Mar 2009 19:41:27 +0000 (13:41 -0600)
committerAlex Rousskov <rousskov@measurement-factory.com>
Sun, 8 Mar 2009 19:41:27 +0000 (13:41 -0600)
ACLFilledChecklist. Context: SourceLayout: acl/, take 1

ACLChecklist contained many data members representing the state of the current
transaction (in a broad sense). These members and related methods depended
on complex types such as HttpRequest and ConnStateData. Any Squid code using
ACLChecklist (and there is a lot of that code) was, hence, dependent on these
types. These dependencies caused, among other things, huge SOURCES lists in
src/Makefile.am, often for trivial targets such as ufsdump and test cases.

ACLChecklist is an abstract class now (to make sure we do not accidentally
create it). ACLChecklist has only one kid: ACLFilledChecklist. The Filled()
global function can be used to cast ACLChecklist* to ACLFilledChecklist*.
Since all ACLChecklist objects have to be ACLFilledChecklist objects, the cast
is fast and safe. The cast allows us to avoid bloating ACLChecklist with
virtual methods that only make sense in ACLFilledChecklist context.

ACLFilledChecklist now contains state-specific members while ACLChecklist
contains basic check list logic. The code that organizes or passes through
ACL checks does not need to be exposed to ACLFilledChecklist and the data
types it depends on.

Furthermore, ACLFilledChecklist should not contain complicated checks either.
It should focus on maintaining the state. The checks should go into specific
ACLs. Otherwise, complex checks cause dependency cycles with higher-level
libraries that provide code for those checks and yet depend on having access
to ACLFilledChecklist to implement specific ACLs. Currently, only the
authenticated() method got moved to auth/Acl.{cc,h} to break the circular
dependency between acl/libs and auth/libs. More work in that direction will
probably be required as we move more src/* code into libraries.

ACLFilledChecklist constructor replaces aclChecklistCreate global. This
simplifies the initiating code of all fast ACL checks: the checks no longer
need to do manual state locking, duplicating aclChecklistCreate code.

src/ACLChecklist.cci [deleted file]
src/acl/Checklist.cc [moved from src/ACLChecklist.cc with 59% similarity]
src/acl/Checklist.h [moved from src/ACLChecklist.h with 73% similarity]
src/acl/FilledChecklist.cc [new file with mode: 0644]
src/acl/FilledChecklist.h [new file with mode: 0644]

diff --git a/src/ACLChecklist.cci b/src/ACLChecklist.cci
deleted file mode 100644 (file)
index 013f8ca..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * $Id$
- *
- * DEBUG: section 28    Access Control
- * AUTHOR: Henrik Nordstrom
- *
- * 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.
- *
- */
-
-bool
-ACLChecklist::matchAclListFast(const ACLList * list)
-{
-    matchAclList(list, true);
-    return finished();
-}
-
similarity index 59%
rename from src/ACLChecklist.cc
rename to src/acl/Checklist.cc
index 4811f5cd48d2ca1020eb3dac4c6d919d92a7f439..17a203c066674367f37d289753f71f60e34cf9b5 100644 (file)
  */
 
 #include "squid.h"
-#include "ACLChecklist.h"
-#include "HttpRequest.h"
-#include "HttpReply.h"
-#include "authenticate.h"
-#include "ACLProxyAuth.h"
-#include "client_side.h"
-#include "auth/UserRequest.h"
-
-int
-ACLChecklist::authenticated()
-{
-    http_hdr_type headertype;
-
-    if (NULL == request) {
-        fatal ("requiresRequest SHOULD have been true for this ACL!!");
-        return 0;
-    } else if (request->flags.accelerated) {
-        /* WWW authorization on accelerated requests */
-        headertype = HDR_AUTHORIZATION;
-    } else if (request->flags.intercepted || request->flags.spoof_client_ip) {
-        debugs(28, DBG_IMPORTANT, HERE << " authentication not applicable on intercepted requests.");
-        return -1;
-    } else {
-        /* Proxy authorization on proxy requests */
-        headertype = HDR_PROXY_AUTHORIZATION;
-    }
-
-    /* get authed here */
-    /* Note: this fills in auth_user_request when applicable */
-    /*
-     * DPW 2007-05-08
-     * tryToAuthenticateAndSetAuthUser used to try to lock and
-     * unlock auth_user_request on our behalf, but it was too
-     * ugly and hard to follow.  Now we do our own locking here.
-     *
-     * I'm not sure what tryToAuthenticateAndSetAuthUser does when
-     * auth_user_request is set before calling.  I'm tempted to
-     * unlock and set it to NULL, but it seems safer to save the
-     * pointer before calling and unlock it afterwards.  If the
-     * pointer doesn't change then its a no-op.
-     */
-    AuthUserRequest *old_auth_user_request = auth_user_request;
-    auth_acl_t result = AuthUserRequest::tryToAuthenticateAndSetAuthUser (&auth_user_request, headertype, request, conn(), src_addr);
-    if (auth_user_request)
-        AUTHUSERREQUESTLOCK(auth_user_request, "ACLChecklist");
-    AUTHUSERREQUESTUNLOCK(old_auth_user_request, "old ACLChecklist");
-    switch (result) {
-
-    case AUTH_ACL_CANNOT_AUTHENTICATE:
-        debugs(28, 4, "aclMatchAcl: returning  0 user authenticated but not authorised.");
-        return 0;
-
-    case AUTH_AUTHENTICATED:
-
-        return 1;
-        break;
-
-    case AUTH_ACL_HELPER:
-        debugs(28, 4, "aclMatchAcl: returning 0 sending credentials to helper.");
-        changeState (ProxyAuthLookup::Instance());
-        return 0;
-
-    case AUTH_ACL_CHALLENGE:
-        debugs(28, 4, "aclMatchAcl: returning 0 sending authentication challenge.");
-        changeState (ProxyAuthNeeded::Instance());
-        return 0;
-
-    default:
-        fatal("unexpected authenticateAuthenticate reply\n");
-        return 0;
-    }
-}
+#include "acl/Checklist.h"
 
 allow_t const &
 ACLChecklist::currentAnswer() const
@@ -249,6 +178,8 @@ ACLChecklist::checkForAsync()
     asyncState()->checkForAsync(this);
 }
 
+// ACLFilledChecklist overwrites this to unclock something before we
+// "delete this"
 void
 ACLChecklist::checkCallback(allow_t answer)
 {
@@ -256,23 +187,6 @@ ACLChecklist::checkCallback(allow_t answer)
     void *cbdata_;
     debugs(28, 3, "ACLChecklist::checkCallback: " << this << " answer=" << answer);
 
-    /* During reconfigure, we can end up not finishing call
-     * sequences into the auth code */
-
-    if (auth_user_request) {
-        /* the checklist lock */
-        AUTHUSERREQUESTUNLOCK(auth_user_request, "ACLChecklist");
-        /* it might have been connection based */
-        assert(conn() != NULL);
-        /*
-         * DPW 2007-05-08
-         * yuck, this make me uncomfortable.  why do this here?
-         * ConnStateData will do its own unlocking.
-         */
-        AUTHUSERREQUESTUNLOCK(conn()->auth_user_request, "conn via ACLChecklist");
-        conn()->auth_type = AUTH_BROKEN;
-    }
-
     callback_ = callback;
     callback = NULL;
 
@@ -346,104 +260,28 @@ ACLChecklist::matchAclList(const ACLList * head, bool const fast)
     PROF_stop(aclMatchAclList);
 }
 
-CBDATA_CLASS_INIT(ACLChecklist);
-
-void *
-ACLChecklist::operator new (size_t size)
-{
-    assert (size == sizeof(ACLChecklist));
-    CBDATA_INIT_TYPE(ACLChecklist);
-    ACLChecklist *result = cbdataAlloc(ACLChecklist);
-    return result;
-}
-
-void
-ACLChecklist::operator delete (void *address)
-{
-    ACLChecklist *t = static_cast<ACLChecklist *>(address);
-    cbdataFree(t);
-}
-
 ACLChecklist::ACLChecklist() :
         accessList (NULL),
-        dst_peer(NULL),
-        request (NULL),
-        reply (NULL),
-        auth_user_request (NULL),
-#if SQUID_SNMP
-        snmp_community(NULL),
-#endif
-#if USE_SSL
-        ssl_error(0),
-#endif
         callback (NULL),
         callback_data (NULL),
-        extacl_entry (NULL),
-        conn_(NULL),
-        fd_(-1),
         async_(false),
         finished_(false),
         allow_(ACCESS_DENIED),
         state_(NullState::Instance()),
-        destinationDomainChecked_(false),
-        sourceDomainChecked_(false),
         lastACLResult_(false)
 {
-    my_addr.SetEmpty();
-    src_addr.SetEmpty();
-    dst_addr.SetEmpty();
-    rfc931[0] = '\0';
 }
 
 ACLChecklist::~ACLChecklist()
 {
     assert (!asyncInProgress());
 
-    if (extacl_entry)
-        cbdataReferenceDone(extacl_entry);
-
-    HTTPMSGUNLOCK(request);
-
-    HTTPMSGUNLOCK(reply);
-
-    // no auth_user_request in builds without any Authentication configured
-    if (auth_user_request)
-        AUTHUSERREQUESTUNLOCK(auth_user_request, "ACLChecklist destructor");
-
-    cbdataReferenceDone(conn_);
-
     cbdataReferenceDone(accessList);
 
     debugs(28, 4, "ACLChecklist::~ACLChecklist: destroyed " << this);
 }
 
 
-ConnStateData *
-ACLChecklist::conn() const
-{
-    return  conn_;
-}
-
-void
-ACLChecklist::conn(ConnStateData *aConn)
-{
-    assert (conn() == NULL);
-    conn_ = cbdataReference(aConn);
-}
-
-int
-ACLChecklist::fd() const
-{
-    return conn_ != NULL ? conn_->fd : fd_;
-}
-
-void
-ACLChecklist::fd(int aDescriptor)
-{
-    assert(!conn() || conn()->fd == aDescriptor);
-    fd_ = aDescriptor;
-}
-
 void
 ACLChecklist::AsyncState::changeState (ACLChecklist *checklist, AsyncState *newState) const
 {
@@ -532,32 +370,6 @@ ACLChecklist::fastCheck()
 }
 
 
-bool
-ACLChecklist::destinationDomainChecked() const
-{
-    return destinationDomainChecked_;
-}
-
-void
-ACLChecklist::markDestinationDomainChecked()
-{
-    assert (!finished() && !destinationDomainChecked());
-    destinationDomainChecked_ = true;
-}
-
-bool
-ACLChecklist::sourceDomainChecked() const
-{
-    return sourceDomainChecked_;
-}
-
-void
-ACLChecklist::markSourceDomainChecked()
-{
-    assert (!finished() && !sourceDomainChecked());
-    sourceDomainChecked_ = true;
-}
-
 bool
 ACLChecklist::checking() const
 {
@@ -570,58 +382,17 @@ ACLChecklist::checking (bool const newValue)
     checking_ = newValue;
 }
 
-/*
- * Any ACLChecklist created by aclChecklistCreate() must eventually be
- * freed by ACLChecklist::operator delete().  There are two common cases:
- *
- * A) Using aclCheckFast():  The caller creates the ACLChecklist using
- *    aclChecklistCreate(), checks it using aclCheckFast(), and frees it
- *    using aclChecklistFree().
- *
- * B) Using aclNBCheck() and callbacks: The caller creates the
- *    ACLChecklist using aclChecklistCreate(), and passes it to
- *    aclNBCheck().  Control eventually passes to ACLChecklist::checkCallback(),
- *    which will invoke the callback function as requested by the
- *    original caller of aclNBCheck().  This callback function must
- *    *not* invoke aclChecklistFree().  After the callback function
- *    returns, ACLChecklist::checkCallback() will free the ACLChecklist using
- *    aclChecklistFree().
- */
-ACLChecklist *
-aclChecklistCreate(const acl_access * A, HttpRequest * request, const char *ident)
+bool
+ACLChecklist::callerGone()
 {
-    // TODO: make this a constructor? On-stack creation uses the same code.
-    ACLChecklist *checklist = new ACLChecklist;
-
-    if (A)
-        checklist->accessList = cbdataReference(A);
-
-    if (request != NULL) {
-        checklist->request = HTTPMSGLOCK(request);
-#if FOLLOW_X_FORWARDED_FOR
-        if (Config.onoff.acl_uses_indirect_client)
-            checklist->src_addr = request->indirect_client_addr;
-        else
-#endif /* FOLLOW_X_FORWARDED_FOR */
-            checklist->src_addr = request->client_addr;
-        checklist->my_addr = request->my_addr;
-    }
-
-#if USE_IDENT
-    if (ident)
-        xstrncpy(checklist->rfc931, ident, USER_IDENT_SZ);
-
-#endif
-
-    return checklist;
+    return !cbdataReferenceValid(callback_data);
 }
 
 bool
-ACLChecklist::callerGone()
+ACLChecklist::matchAclListFast(const ACLList * list)
 {
-    return !cbdataReferenceValid(callback_data);
+    matchAclList(list, true);
+    return finished();
 }
 
-#ifndef _USE_INLINE_
-#include "ACLChecklist.cci"
-#endif
+
similarity index 73%
rename from src/ACLChecklist.h
rename to src/acl/Checklist.h
index 850d3a9d9a5c70463196b976408e3ebdb68a9e40..7a46ecc2a2abf2c2936361206bf70fa3d2e052c4 100644 (file)
 #ifndef SQUID_ACLCHECKLIST_H
 #define SQUID_ACLCHECKLIST_H
 
-//#include "typedefs.h"
-//#include "client_side.h"
-//#include "structs.h"
+#include "acl/Acl.h"
 
-#include "ACL.h"
-
-class AuthUserRequest;
-class ExternalACLEntry;
-class ConnStateData;
-
-/// \ingroup ACLAPI
+/** \ingroup ACLAPI
+    Base class for maintaining Squid and transaction state for access checks.
+       Provides basic ACL checking methods. Its only child, ACLFilledChecklist,
+       keeps the actual state data. The split is necessary to avoid exposing
+    all ACL-related code to virtually Squid data types. */
 class ACLChecklist
 {
 
@@ -88,18 +84,9 @@ public:
     };
 
 
-public: /* operators */
-    void *operator new(size_t);
-    void operator delete(void *);
-
+public:
     ACLChecklist();
-    ~ACLChecklist();
-    /** NP: To cause link failures if assignment attempted */
-    ACLChecklist (ACLChecklist const &);
-    /** NP: To cause link failures if assignment attempted */
-    ACLChecklist &operator=(ACLChecklist const &);
-
-public: /* API methods */
+    virtual ~ACLChecklist();
 
     /**
      * Trigger off a non-blocking access check for a set of *_access options..
@@ -135,7 +122,7 @@ public: /* API methods */
      * \retval  1/true    Access Allowed
      * \retval 0/false    Access Denied
      */
-    _SQUID_INLINE_ bool matchAclListFast(const ACLList * list);
+    bool matchAclListFast(const ACLList * list);
 
     /**
      * Attempt to check the current checklist against current data.
@@ -149,20 +136,6 @@ public: /* API methods */
      */
     void check();
 
-    ConnStateData * conn() const;
-
-    /// uses conn() if available
-    int fd() const;
-
-    /// set either conn
-    void conn(ConnStateData *);
-    /// set FD
-    void fd(int aDescriptor);
-
-/* Accessors used by internal ACL stuff */
-
-    int authenticated();
-
     bool asyncInProgress() const;
     void asyncInProgress(bool const);
 
@@ -175,62 +148,33 @@ public: /* API methods */
     void changeState(AsyncState *);
     AsyncState *asyncState() const;
 
-private: /* NP: only used internally */
+       // 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
+       virtual bool hasRequest() const = 0;
+       virtual bool hasReply() const = 0;
 
-    void checkCallback(allow_t answer);
+private:
+    virtual void checkCallback(allow_t answer);
     void checkAccessList();
     void checkForAsync();
 
-public: /* checklist available data */
-
+public:
     const acl_access *accessList;
 
-    IpAddress src_addr;
-
-    IpAddress dst_addr;
-
-    IpAddress my_addr;
-
-    struct peer *dst_peer;
-
-    HttpRequest *request;
-
-    /* for acls that look at reply data */
-    HttpReply *reply;
-    char rfc931[USER_IDENT_SZ];
-    AuthUserRequest *auth_user_request;
-#if SQUID_SNMP
-
-    char *snmp_community;
-#endif
-
-#if USE_SSL
-    int ssl_error;
-#endif
-
     PF *callback;
     void *callback_data;
-    ExternalACLEntry *extacl_entry;
-
-    bool destinationDomainChecked() const;
-    void markDestinationDomainChecked();
-    bool sourceDomainChecked() const;
-    void markSourceDomainChecked();
 
 private: /* internal methods */
     void preCheck();
     void matchAclList(const ACLList * list, bool const fast);
     void matchAclListSlow(const ACLList * list);
-    CBDATA_CLASS(ACLChecklist);
 
-    ConnStateData * conn_;          /**< hack for ident and NTLM */
-    int fd_;                        /**< may be available when conn_ is not */
     bool async_;
     bool finished_;
     allow_t allow_;
     AsyncState *state_;
-    bool destinationDomainChecked_;
-    bool sourceDomainChecked_;
+
     bool checking_;
     bool checking() const;
     void checking (bool const);
@@ -244,13 +188,4 @@ public:
     bool lastACLResult() const { return lastACLResult_; }
 };
 
-/// \ingroup ACLAPI
-SQUIDCEXTERN ACLChecklist *aclChecklistCreate(const acl_access *,
-        HttpRequest *,
-        const char *ident);
-
-#ifdef _USE_INLINE_
-#include "ACLChecklist.cci"
-#endif
-
 #endif /* SQUID_ACLCHECKLIST_H */
diff --git a/src/acl/FilledChecklist.cc b/src/acl/FilledChecklist.cc
new file mode 100644 (file)
index 0000000..e27a00d
--- /dev/null
@@ -0,0 +1,274 @@
+#include "squid.h"
+#include "HttpRequest.h"
+#include "HttpReply.h"
+#include "client_side.h"
+#include "auth/UserRequest.h"
+#include "auth/AclProxyAuth.h"
+#include "acl/FilledChecklist.h"
+
+CBDATA_CLASS_INIT(ACLFilledChecklist);
+
+#if MOVED
+int
+ACLFilledChecklist::authenticated()
+{
+    http_hdr_type headertype;
+
+    if (NULL == request) {
+        fatal ("requiresRequest SHOULD have been true for this ACL!!");
+        return 0;
+    } else if (request->flags.accelerated) {
+        /* WWW authorization on accelerated requests */
+        headertype = HDR_AUTHORIZATION;
+    } else if (request->flags.intercepted || request->flags.spoof_client_ip) {
+        debugs(28, DBG_IMPORTANT, HERE << " authentication not applicable on intercepted requests.");
+        return -1;
+    } else {
+        /* Proxy authorization on proxy requests */
+        headertype = HDR_PROXY_AUTHORIZATION;
+    }
+
+    /* get authed here */
+    /* Note: this fills in auth_user_request when applicable */
+    /*
+     * DPW 2007-05-08
+     * tryToAuthenticateAndSetAuthUser used to try to lock and
+     * unlock auth_user_request on our behalf, but it was too
+     * ugly and hard to follow.  Now we do our own locking here.
+     *
+     * I'm not sure what tryToAuthenticateAndSetAuthUser does when
+     * auth_user_request is set before calling.  I'm tempted to
+     * unlock and set it to NULL, but it seems safer to save the
+     * pointer before calling and unlock it afterwards.  If the
+     * pointer doesn't change then its a no-op.
+     */
+    AuthUserRequest *old_auth_user_request = auth_user_request;
+    auth_acl_t result = AuthUserRequest::tryToAuthenticateAndSetAuthUser (&auth_user_request, headertype, request, conn(), src_addr);
+    if (auth_user_request)
+        AUTHUSERREQUESTLOCK(auth_user_request, "ACLFilledChecklist");
+    AUTHUSERREQUESTUNLOCK(old_auth_user_request, "old ACLFilledChecklist");
+    switch (result) {
+
+    case AUTH_ACL_CANNOT_AUTHENTICATE:
+        debugs(28, 4, "aclMatchAcl: returning  0 user authenticated but not authorised.");
+        return 0;
+
+    case AUTH_AUTHENTICATED:
+
+        return 1;
+        break;
+
+    case AUTH_ACL_HELPER:
+        debugs(28, 4, "aclMatchAcl: returning 0 sending credentials to helper.");
+        changeState (ProxyAuthLookup::Instance());
+        return 0;
+
+    case AUTH_ACL_CHALLENGE:
+        debugs(28, 4, "aclMatchAcl: returning 0 sending authentication challenge.");
+        changeState (ProxyAuthNeeded::Instance());
+        return 0;
+
+    default:
+        fatal("unexpected authenticateAuthenticate reply\n");
+        return 0;
+    }
+}
+#endif
+
+void
+ACLFilledChecklist::checkCallback(allow_t answer)
+{
+    debugs(28, 5, "ACLFilledChecklist::checkCallback: " << this << " answer=" << answer);
+
+    /* During reconfigure, we can end up not finishing call
+     * sequences into the auth code */
+
+    if (auth_user_request) {
+        /* the filled_checklist lock */
+        AUTHUSERREQUESTUNLOCK(auth_user_request, "ACLFilledChecklist");
+        /* it might have been connection based */
+        assert(conn() != NULL);
+        /*
+         * DPW 2007-05-08
+         * yuck, this make me uncomfortable.  why do this here?
+         * ConnStateData will do its own unlocking.
+         */
+        AUTHUSERREQUESTUNLOCK(conn()->auth_user_request, "conn via ACLFilledChecklist");
+        conn()->auth_type = AUTH_BROKEN;
+    }
+
+       ACLFilledChecklist::checkCallback(answer); // may delete us
+}
+
+
+void *
+ACLFilledChecklist::operator new (size_t size)
+{
+    assert (size == sizeof(ACLFilledChecklist));
+    CBDATA_INIT_TYPE(ACLFilledChecklist);
+    ACLFilledChecklist *result = cbdataAlloc(ACLFilledChecklist);
+    return result;
+}
+
+void
+ACLFilledChecklist::operator delete (void *address)
+{
+    ACLFilledChecklist *t = static_cast<ACLFilledChecklist *>(address);
+    cbdataFree(t);
+}
+
+
+ACLFilledChecklist::ACLFilledChecklist() :
+        dst_peer(NULL),
+        request (NULL),
+        reply (NULL),
+        auth_user_request (NULL),
+#if SQUID_SNMP
+        snmp_community(NULL),
+#endif
+#if USE_SSL
+        ssl_error(0),
+#endif
+        extacl_entry (NULL),
+        conn_(NULL),
+        fd_(-1),
+        destinationDomainChecked_(false),
+        sourceDomainChecked_(false)
+{
+    my_addr.SetEmpty();
+    src_addr.SetEmpty();
+    dst_addr.SetEmpty();
+    rfc931[0] = '\0';
+}
+
+
+ACLFilledChecklist::~ACLFilledChecklist()
+{
+    assert (!asyncInProgress());
+
+    if (extacl_entry)
+        cbdataReferenceDone(extacl_entry);
+
+    HTTPMSGUNLOCK(request);
+
+    HTTPMSGUNLOCK(reply);
+
+    // no auth_user_request in builds without any Authentication configured
+    if (auth_user_request)
+        AUTHUSERREQUESTUNLOCK(auth_user_request, "ACLFilledChecklist destructor");
+
+    cbdataReferenceDone(conn_);
+
+    debugs(28, 4, HERE << "ACLFilledChecklist destroyed " << this);
+}
+
+
+ConnStateData *
+ACLFilledChecklist::conn() const
+{
+    return  conn_;
+}
+
+void
+ACLFilledChecklist::conn(ConnStateData *aConn)
+{
+    assert (conn() == NULL);
+    conn_ = cbdataReference(aConn);
+}
+
+int
+ACLFilledChecklist::fd() const
+{
+    return conn_ != NULL ? conn_->fd : fd_;
+}
+
+void
+ACLFilledChecklist::fd(int aDescriptor)
+{
+    assert(!conn() || conn()->fd == aDescriptor);
+    fd_ = aDescriptor;
+}
+
+bool
+ACLFilledChecklist::destinationDomainChecked() const
+{
+    return destinationDomainChecked_;
+}
+
+void
+ACLFilledChecklist::markDestinationDomainChecked()
+{
+    assert (!finished() && !destinationDomainChecked());
+    destinationDomainChecked_ = true;
+}
+
+bool
+ACLFilledChecklist::sourceDomainChecked() const
+{
+    return sourceDomainChecked_;
+}
+
+void
+ACLFilledChecklist::markSourceDomainChecked()
+{
+    assert (!finished() && !sourceDomainChecked());
+    sourceDomainChecked_ = true;
+}
+
+/*
+ * There are two common ACLFilledChecklist lifecycles paths:
+ *
+ * A) Using aclCheckFast(): The caller creates an ACLFilledChecklist object
+ *    on stack and calls aclCheckFast().
+ *
+ * B) Using aclNBCheck() and callbacks: The caller allocates an
+ *    ACLFilledChecklist object (via operator new) and passes it to
+ *    aclNBCheck(). Control eventually passes to ACLChecklist::checkCallback(),
+ *    which will invoke the callback function as requested by the
+ *    original caller of aclNBCheck().  This callback function must
+ *    *not* delete the list.  After the callback function returns,
+ *    checkCallback() will delete the list (i.e., self).
+ */
+ACLFilledChecklist::ACLFilledChecklist(const acl_access *A, HttpRequest *request, const char *ident):
+    dst_peer(NULL),
+    request(NULL),
+    reply(NULL),
+    auth_user_request(NULL),
+#if SQUID_SNMP
+    snmp_community(NULL),
+#endif
+#if USE_SSL
+    ssl_error(0),
+#endif
+    extacl_entry (NULL),
+    conn_(NULL),
+    fd_(-1),
+    destinationDomainChecked_(false),
+    sourceDomainChecked_(false)
+{
+    my_addr.SetEmpty();
+    src_addr.SetEmpty();
+    dst_addr.SetEmpty();
+    rfc931[0] = '\0';
+    
+    // cbdataReferenceDone() is in either fastCheck() or the destructor
+    if (A)
+        accessList = cbdataReference(A);
+
+    if (request != NULL) {
+        request = HTTPMSGLOCK(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 USE_IDENT
+    if (ident)
+        xstrncpy(rfc931, ident, USER_IDENT_SZ);
+#endif
+}
+
diff --git a/src/acl/FilledChecklist.h b/src/acl/FilledChecklist.h
new file mode 100644 (file)
index 0000000..7211c7f
--- /dev/null
@@ -0,0 +1,94 @@
+#ifndef SQUID_ACLFILLED_CHECKLIST_H
+#define SQUID_ACLFILLED_CHECKLIST_H
+
+#include "acl/Checklist.h"
+
+class AuthUserRequest;
+class ExternalACLEntry;
+class ConnStateData;
+
+/** \ingroup ACLAPI
+    ACLChecklist filled with specific data, representing Squid and transaction
+    state for access checks along with some data-specific checking methods */
+class ACLFilledChecklist: public ACLChecklist
+{
+public:
+    void *operator new(size_t);
+    void operator delete(void *);
+
+    ACLFilledChecklist();
+       ACLFilledChecklist(const acl_access *, HttpRequest *, const char *ident);
+    ~ACLFilledChecklist();
+
+public:
+    ConnStateData * conn() const;
+
+    /// uses conn() if available
+    int fd() const;
+
+    /// set either conn
+    void conn(ConnStateData *);
+    /// set 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; }
+
+public:
+    IpAddress src_addr;
+    IpAddress dst_addr;
+    IpAddress my_addr;
+    struct peer *dst_peer;
+
+    HttpRequest *request;
+    HttpReply *reply;
+
+    char rfc931[USER_IDENT_SZ];
+    AuthUserRequest *auth_user_request;
+
+#if SQUID_SNMP
+    char *snmp_community;
+#endif
+
+#if USE_SSL
+    int ssl_error;
+#endif
+
+    ExternalACLEntry *extacl_entry;
+
+private:
+    virtual void checkCallback(allow_t answer);
+
+private:
+    CBDATA_CLASS(ACLFilledChecklist);
+
+    ConnStateData * conn_;          /**< hack for ident and NTLM */
+    int fd_;                        /**< may be available when conn_ is not */
+    bool destinationDomainChecked_;
+    bool sourceDomainChecked_;
+
+private:
+    /// not implemented; will cause link failures if used
+    ACLFilledChecklist(const ACLFilledChecklist &);
+    /// not implemented; will cause link failures if used
+    ACLFilledChecklist &operator=(const ACLFilledChecklist &);
+};
+
+/// convenience and safety wrapper for dynamic_cast<ACLFilledChecklist*>
+inline
+ACLFilledChecklist *Filled(ACLChecklist *checklist)
+{
+    // this should always be safe because ACLChecklist is an abstract class
+    // and ACLFilledChecklist is its only [concrete] child
+    return dynamic_cast<ACLFilledChecklist*>(checklist);
+}
+
+#endif /* SQUID_ACLFILLED_CHECKLIST_H */