]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Support client connection annotation by helpers via clt_conn_tag=TAG.
authorChristos Tsantilas <chtsanti@users.sourceforge.net>
Sat, 12 Jul 2014 09:34:43 +0000 (12:34 +0300)
committerChristos Tsantilas <chtsanti@users.sourceforge.net>
Sat, 12 Jul 2014 09:34:43 +0000 (12:34 +0300)
TCP client connections tagging is useful for faking various forms of
connection-based "authentication" when standard HTTP authentication cannot be
used. A URL rewriter or, external ACL helper may mark the "authenticated"
client connection to avoid going through "authentication" steps during
subsequent requests on the same connection and to share connection
"authentication" information with Squid ACLs, other helpers, and logs.

After this change, Squid accepts optional clt_conn_tag=TAG pair from a
helper and associates the received TAG with the client TCP connection.
Squid treats the received clt_conn_tag=TAG pair as a regular annotation, but
also keeps it across all requests on the same client connection. A helper may
update the client connection TAG value during subsequent requests.

Also after this patch the notes comming from helpers replaces any existing
note values.

This is a Measurement Factory project

src/Notes.cc
src/Notes.h
src/auth/UserRequest.cc
src/cf.data.pre
src/client_side.h
src/client_side_request.cc
src/external_acl.cc

index 15e4defc8ab6d30de0b341e3801893f47d6714e7..9ff09ce7c98032b9f6463c818840b54b0f38a286 100644 (file)
@@ -31,6 +31,7 @@
 #include "AccessLogEntry.h"
 #include "acl/FilledChecklist.h"
 #include "acl/Gadgets.h"
+#include "client_side.h"
 #include "ConfigParser.h"
 #include "globals.h"
 #include "HttpReply.h"
@@ -202,6 +203,20 @@ NotePairs::add(const char *key, const char *note)
     entries.push_back(new NotePairs::Entry(key, note));
 }
 
+void
+NotePairs::remove(const char *key)
+{
+    std::vector<NotePairs::Entry *>::iterator i = entries.begin();
+    while(i != entries.end()) {
+        if ((*i)->name.cmp(key) == 0) {
+            delete *i;
+            i = entries.erase(i);
+        } else {
+            ++i;
+        }
+    }
+}
+
 void
 NotePairs::addStrList(const char *key, const char *values)
 {
@@ -243,6 +258,15 @@ NotePairs::appendNewOnly(const NotePairs *src)
     }
 }
 
+void
+NotePairs::replaceOrAdd(const NotePairs *src)
+{
+    for (std::vector<NotePairs::Entry *>::const_iterator  i = src->entries.begin(); i != src->entries.end(); ++i) {
+        remove((*i)->name.termedBuf());
+    }
+    append(src);
+}
+
 NotePairs &
 SyncNotes(AccessLogEntry &ale, HttpRequest &request)
 {
@@ -258,3 +282,16 @@ SyncNotes(AccessLogEntry &ale, HttpRequest &request)
     }
     return *ale.notes;
 }
+
+void
+UpdateRequestNotes(ConnStateData *csd, HttpRequest &request, NotePairs const &helperNotes)
+{
+    // Tag client connection if the helper responded with clt_conn_tag=tag.
+    if (const char *connTag = helperNotes.findFirst("clt_conn_tag")) {
+        if (csd)
+            csd->connectionTag(connTag);
+    }
+    if (!request.notes)
+        request.notes = new NotePairs;
+    request.notes->replaceOrAdd(&helperNotes);
+}
index 6fc0c71538434f8e690cf7db8bc0912f352e22bb..8ea82e432c8d2377d3a4776f38beefde519da9cb 100644 (file)
@@ -135,6 +135,12 @@ public:
      */
     void append(const NotePairs *src);
 
+    /**
+     * Replace existing list entries with the src NotePairs entries.
+     * Entries which do not exist in the destination set are added.
+     */
+    void replaceOrAdd(const NotePairs *src);
+
     /**
      * Append any new entries of the src NotePairs list to our list.
      * Entries which already exist in the destination set are ignored.
@@ -159,6 +165,11 @@ public:
      */
     void add(const char *key, const char *value);
 
+    /**
+     * Remove all notes with a given key.
+     */
+    void remove(const char *key);
+
     /**
      * Adds a note key and values strList to the notes list.
      * If the key name already exists in list, add the new values to its set
@@ -197,4 +208,9 @@ class AccessLogEntry;
  */
 NotePairs &SyncNotes(AccessLogEntry &ale, HttpRequest &request);
 
+class ConnStateData;
+/**
+ * Updates ConnStateData ids and HttpRequest notes from helpers received notes.
+ */
+void UpdateRequestNotes(ConnStateData *csd, HttpRequest &request, NotePairs const &notes);
 #endif
index 5794be141972a92accf0c394340f1d9ceec3431c..9d92db53c05ba557cf3df76aff7439ce091954b9 100644 (file)
@@ -264,10 +264,8 @@ authTryGetUser(Auth::UserRequest::Pointer auth_user_request, ConnStateData * con
         // XXX: we have no access to the transaction / AccessLogEntry so cant SyncNotes().
         // workaround by using anything already set in HttpRequest
         // OR use new and rely on a later Sync copying these to AccessLogEntry
-        if (!request->notes)
-            request->notes = new NotePairs;
 
-        request->notes->appendNewOnly(&res->user()->notes);
+        UpdateRequestNotes(conn, *request, res->user()->notes);
     }
 
     return res;
index 3db03a3416c6bfeeb7f79c17de9d8c71fab2671e..102d954baa1df74dfc9572836dc124b14b56aeea 100644 (file)
@@ -716,6 +716,10 @@ DOC_START
          log=          String to be logged in access.log. Available as
                        %ea in logformat specifications.
 
+         clt_conn_tag= Associates a TAG with the client TCP connection.
+                       Please see url_rewrite_program related documentation for
+                       this kv-pair.
+
        Any keywords may be sent on any response whether OK, ERR or BH.
 
        All response keyword values need to be a single token with URL
@@ -4590,7 +4594,8 @@ DOC_START
 
          [channel-ID <SP>] URL [<SP> extras]<NL>
 
-
+       See url_rewrite_extras on how to send "extras" with optional values to
+       the helper.
        After processing the request the helper must reply using the following format:
 
          [channel-ID <SP>] result [<SP> kv-pairs]
@@ -4622,10 +4627,14 @@ DOC_START
                reserved for delivering a log message.
 
 
-       In the future, the interface protocol will be extended with
-       key=value pairs ("kv-pairs" shown above).  Helper programs
-       should be prepared to receive and possibly ignore additional
-       whitespace-separated tokens on each input line.
+       In addition to the above kv-pairs Squid also understands the following
+       optional kv-pairs received from URL rewriters:
+         clt_conn_tag=TAG
+               Associates a TAG with the client TCP connection.
+               The TAG is treated as a regular annotation but persists across
+               future requests on the client connection rather than just the
+               current request. A helper may update the TAG during subsequent
+               requests be returning a new kv-pair.
 
        When using the concurrency= option the protocol is changed by
        introducing a query channel tag in front of the request/response.
@@ -4782,6 +4791,12 @@ DOC_START
                An internal error occured in the helper, preventing
                a result being identified.
 
+       In addition to the above kv-pairs Squid also understands the following
+       optional kv-pairs received from URL rewriters:
+         clt_conn_tag=TAG
+               Associates a TAG with the client TCP connection.
+               Please see url_rewrite_program related documentation for this
+               kv-pair
 
        Helper programs should be prepared to receive and possibly ignore
        additional whitespace-separated tokens on each input line.
index f48c73c4321f897180bde4afc8a1b6d31e92edae..91139dac8f2d0bf22970eb00ea2b6b0912aabf81 100644 (file)
@@ -381,6 +381,10 @@ public:
     bool switchedToHttps() const { return false; }
 #endif
 
+    /* clt_conn_tag=tag annotation access */
+    const SBuf &connectionTag() const { return connectionTag_; }
+    void connectionTag(const char *aTag) { connectionTag_ = aTag; }
+
 protected:
     void startDechunkingRequest();
     void finishDechunkingRequest(bool withSuccess);
@@ -424,6 +428,8 @@ private:
     AsyncCall::Pointer reader; ///< set when we are reading
     BodyPipe::Pointer bodyPipe; // set when we are reading request body
 
+    SBuf connectionTag_; ///< clt_conn_tag=Tag annotation for client connection
+
     CBDATA_CLASS2(ConnStateData);
 };
 
index 2b85580edc1494503fb3fa0e68deca7ff8068efe..ea434ecc0fd75d2979385b560789db80ba3cf78f 100644 (file)
@@ -1239,10 +1239,10 @@ ClientRequestContext::clientRedirectDone(const HelperReply &reply)
 
     // Put helper response Notes into the transaction state record (ALE) eventually
     // do it early to ensure that no matter what the outcome the notes are present.
-    if (http->al != NULL) {
-        NotePairs &notes = SyncNotes(*http->al, *old_request);
-        notes.append(&reply.notes);
-    }
+    if (http->al != NULL)
+        (void)SyncNotes(*http->al, *old_request);
+
+    UpdateRequestNotes(http->getConn(), *old_request, reply.notes);
 
     switch (reply.result) {
     case HelperReply::Unknown:
@@ -1360,10 +1360,10 @@ ClientRequestContext::clientStoreIdDone(const HelperReply &reply)
 
     // Put helper response Notes into the transaction state record (ALE) eventually
     // do it early to ensure that no matter what the outcome the notes are present.
-    if (http->al != NULL) {
-        NotePairs &notes = SyncNotes(*http->al, *old_request);
-        notes.append(&reply.notes);
-    }
+    if (http->al != NULL)
+        (void)SyncNotes(*http->al, *old_request);
+
+    UpdateRequestNotes(http->getConn(), *old_request, reply.notes);
 
     switch (reply.result) {
     case HelperReply::Unknown:
@@ -1687,6 +1687,13 @@ ClientHttpRequest::doCallouts()
     if (!calloutContext->http->al->request) {
         calloutContext->http->al->request = request;
         HTTPMSGLOCK(calloutContext->http->al->request);
+
+        NotePairs &notes = SyncNotes(*calloutContext->http->al, *calloutContext->http->request);
+        // Make the previously set client connection ID available as annotation.
+        if (ConnStateData *csd = calloutContext->http->getConn()) {
+            if (!csd->connectionTag().isEmpty())
+                notes.add("clt_conn_tag", SBuf(csd->connectionTag()).c_str());
+        }
     }
 
     if (!calloutContext->error) {
index d4b81d1689581b76d9112303a98ff26986f67aba..450aa34535b9f509a431234550117f710e265fde 100644 (file)
@@ -1534,10 +1534,8 @@ ExternalACLLookup::LookupDone(void *data, void *result)
             // XXX: we have no access to the transaction / AccessLogEntry so cant SyncNotes().
             // workaround by using anything already set in HttpRequest
             // OR use new and rely on a later Sync copying these to AccessLogEntry
-            if (!req->notes)
-                req->notes = new NotePairs;
 
-            req->notes->appendNewOnly(&checklist->extacl_entry->notes);
+            UpdateRequestNotes(checklist->conn(), *req, checklist->extacl_entry->notes);
         }
     }