------------------------------------------------------------ revno: 13486 revision-id: chtsanti@users.sourceforge.net-20140712093443-rc7zno6ktad74zhl parent: squidadm@squid-cache.org-20140711001530-bc8w064l1ehz10e4 committer: Christos Tsantilas branch nick: trunk timestamp: Sat 2014-07-12 12:34:43 +0300 message: Support client connection annotation by helpers via clt_conn_tag=TAG. 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 ------------------------------------------------------------ # Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: chtsanti@users.sourceforge.net-20140712093443-\ # rc7zno6ktad74zhl # target_branch: http://bzr.squid-cache.org/bzr/squid3/trunk/ # testament_sha1: 5c5e2caf67eecba7afe1351118985e75e00956a1 # timestamp: 2014-07-12 10:00:12 +0000 # source_branch: http://bzr.squid-cache.org/bzr/squid3/trunk/ # base_revision_id: squidadm@squid-cache.org-20140711001530-\ # bc8w064l1ehz10e4 # # Begin patch === modified file 'src/Notes.cc' --- src/Notes.cc 2014-06-24 22:52:53 +0000 +++ src/Notes.cc 2014-07-12 09:34:43 +0000 @@ -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" @@ -203,6 +204,20 @@ } void +NotePairs::remove(const char *key) +{ + std::vector::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) { String strValues(values); @@ -243,6 +258,15 @@ } } +void +NotePairs::replaceOrAdd(const NotePairs *src) +{ + for (std::vector::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 @@ } 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); +} === modified file 'src/Notes.h' --- src/Notes.h 2014-06-24 22:52:53 +0000 +++ src/Notes.h 2014-07-12 09:34:43 +0000 @@ -136,6 +136,12 @@ 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. */ @@ -160,6 +166,11 @@ 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 * of values. @@ -197,4 +208,9 @@ */ 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 ¬es); #endif === modified file 'src/auth/UserRequest.cc' --- src/auth/UserRequest.cc 2014-05-22 09:12:48 +0000 +++ src/auth/UserRequest.cc 2014-07-12 09:34:43 +0000 @@ -264,10 +264,8 @@ // 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; === modified file 'src/cf.data.pre' --- src/cf.data.pre 2014-06-24 22:52:53 +0000 +++ src/cf.data.pre 2014-07-12 09:34:43 +0000 @@ -716,6 +716,10 @@ 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 @@ [channel-ID ] URL [ extras] - + 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 ] result [ kv-pairs] @@ -4622,10 +4627,14 @@ 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 @@ 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. === modified file 'src/client_side.h' --- src/client_side.h 2014-06-25 23:21:28 +0000 +++ src/client_side.h 2014-07-12 09:34:43 +0000 @@ -381,6 +381,10 @@ 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 @@ 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); }; === modified file 'src/client_side_request.cc' --- src/client_side_request.cc 2014-06-24 22:52:53 +0000 +++ src/client_side_request.cc 2014-07-12 09:34:43 +0000 @@ -1239,10 +1239,10 @@ // 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 ¬es = 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 @@ // 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 ¬es = 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 @@ if (!calloutContext->http->al->request) { calloutContext->http->al->request = request; HTTPMSGLOCK(calloutContext->http->al->request); + + NotePairs ¬es = 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) { === modified file 'src/external_acl.cc' --- src/external_acl.cc 2014-05-15 07:32:10 +0000 +++ src/external_acl.cc 2014-07-12 09:34:43 +0000 @@ -1534,10 +1534,8 @@ // 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); } }