On 11/24/2012 05:53 PM, Tsantilas Christos wrote:
> I am sending an updated patch for cert validation cache.
> 
> This is a patch over the latest "SSL server certificate validator
> implementation" (cert_validator-v3.patch) I posted in this mailing list

I am reposting with patch attached.

> 
> 
> 
> On 11/14/2012 03:12 PM, Tsantilas Christos wrote:
>> This patch add squid internal cache for cert validator helper. It is
>> posted as separate patch because investigates the LruMap template class
>> which can be used to implement object caches inside squid.
>>
>> The new LruMap template class used to replace old
>> Ssl::LocalContextStorage class which is used as SSL contexts cache.
>>
>> Patch description:
>>
>> This patch add cache to cert validation helper. The following new
>> options  added to "sslcrtvalidator_program" configuration parameter to
>> control cache behaviour:
>>  ttl=n  TTL in seconds for cached results.The default is 60 secs
>>  cache=n  limit the result cache size. The default value is 2048
>>  SSL contexts cache.
>>
>> This is a Measurement Factory project
>>
> 
> 

cert validation cache

This patch add cache to cert validation helper. The following new options 
added to "sslcrtvalidator_program" configuration parameter to control cache
behaviour:
   ttl=n         TTL in seconds for cached results.The default is 60 secs
   cache=n       limit the result cache size. The default value is 2048

To implement the cert validation cache a new template class investigated,
the LruMap which implements a simple lru cache.

The LruMap templete class also used to replace the old Ssl::LocalContextStorage
class which implements a SSL contexts cache.

This is a Measurement Factory project


=== added file 'src/LruMap.h'
--- src/LruMap.h	1970-01-01 00:00:00 +0000
+++ src/LruMap.h	2012-11-24 15:48:48 +0000
@@ -0,0 +1,211 @@
+
+/*
+ */
+
+#ifndef SQUID_LRUMAP_H
+#define SQUID_LRUMAP_H
+
+#include "SquidTime.h"
+#if HAVE_LIST
+#include <list>
+#endif
+#if HAVE_MAP
+#include <map>
+#endif
+
+template <class EntryValue, size_t EntryCost = sizeof(EntryValue)> class LruMap
+{
+public:
+    class Entry
+    {
+    public:
+        Entry(const char *aKey, EntryValue *t): key(aKey), value(t), date(squid_curtime) {}
+        ~Entry() {delete value;}
+    private:
+        Entry(LruMap<EntryValue, EntryCost>::Entry &);
+        LruMap<EntryValue, EntryCost>::Entry & operator = (LruMap<EntryValue, EntryCost>::Entry &);
+    public:
+        std::string key; ///< the key of entry
+        EntryValue *value; ///< A pointer to the stored value
+        time_t date; ///< The date the entry created
+    };
+    typedef std::list<Entry *> Queue;
+    typedef typename std::list<Entry *>::iterator QueueIterator;
+    
+    /// key:queue_item mapping for fast lookups by key
+    typedef std::map<std::string, QueueIterator> Map;
+    typedef typename Map::iterator MapIterator;
+    typedef std::pair<std::string, QueueIterator> MapPair;
+
+
+    LruMap(int ttl, size_t size);
+    ~LruMap();
+    /// Search for an entry, and return a pointer
+    EntryValue *get(const char *key);
+    /// Add an entry to the map
+    bool add(const char *key, EntryValue *t);
+    /// Delete an entry from the map
+    bool del(const char *key);
+    /// (Re-)set the maximum size for this map
+    void setMemLimit(size_t aSize);
+    /// The available size for the map
+    size_t memLimit() const {return memLimit_;}
+    /// The free space of the map
+    size_t freeMem() const { return (memLimit() - size());}
+    /// The current size of the map
+    size_t size() const {return (entries_ * EntryCost);}
+    /// The number of stored entries
+    int entries() const {return entries_;}
+private:
+    LruMap(LruMap<EntryValue, EntryCost> const &);
+    LruMap<EntryValue, EntryCost> & operator = (LruMap<EntryValue, EntryCost> const &);
+    
+    bool expired(Entry &e);
+    void trim();
+    void touch(const MapIterator &i);
+    bool del(const MapIterator &i);
+    void findEntry(const char *key, LruMap::MapIterator &i);
+
+    Map storage; ///< The Key/value * pairs
+    Queue index; ///< LRU cache index
+    int ttl;///< >0 ttl for caching, == 0 cache is disabled, < 0 store for ever
+    size_t memLimit_; ///< The maximum memory to use
+    int entries_; ///< The stored entries
+};
+
+template <class EntryValue, size_t EntryCost>
+LruMap<EntryValue, EntryCost>::LruMap(int aTtl, size_t aSize): entries_(0)
+{
+    ttl = aTtl;
+
+    setMemLimit(aSize);
+}
+
+template <class EntryValue, size_t EntryCost>
+LruMap<EntryValue, EntryCost>::~LruMap()
+{
+    for (QueueIterator i = index.begin(); i != index.end(); ++i) {
+        delete *i;
+    }
+}
+
+template <class EntryValue, size_t EntryCost>
+void
+LruMap<EntryValue, EntryCost>::setMemLimit(size_t aSize)
+{
+    if (aSize > 0)
+        memLimit_ = aSize;
+    else
+        memLimit_ = 0;
+}
+
+template <class EntryValue, size_t EntryCost>
+void
+LruMap<EntryValue, EntryCost>::findEntry(const char *key, LruMap::MapIterator &i)
+{
+    i = storage.find(key);
+    if (i == storage.end()) {
+        return;
+    }
+    index.push_front(*(i->second));
+    index.erase(i->second);
+    i->second = index.begin();
+    
+    LruMap::Entry *e = *i->second;
+
+    if (e && expired(*e)) {
+        del(i);
+        e = NULL;
+    }
+}
+
+template <class EntryValue, size_t EntryCost>
+EntryValue *
+LruMap<EntryValue, EntryCost>::get(const char *key)
+{
+    LruMap::MapIterator i;
+    findEntry(key, i);
+    LruMap::Entry *e = *i->second;
+    if (i != storage.end()) {
+        touch(i);
+        return e->value;
+    }
+    return NULL;
+}
+
+template <class EntryValue, size_t EntryCost>
+bool
+LruMap<EntryValue, EntryCost>::add(const char *key, EntryValue *t)
+{
+    if (ttl == 0)
+        return false;
+
+    del(key);
+    trim();
+    index.push_front(new Entry(key, t));
+    storage.insert(MapPair(key, index.begin()));
+
+    ++entries_;
+    return true;
+}
+
+template <class EntryValue, size_t EntryCost>
+bool
+LruMap<EntryValue, EntryCost>::expired(LruMap::Entry &entry)
+{
+    if (ttl < 0)
+        return false;
+
+    return (entry.date + ttl < squid_curtime);
+}
+
+template <class EntryValue, size_t EntryCost>
+bool
+LruMap<EntryValue, EntryCost>::del(LruMap::MapIterator const &i)
+{
+     if (i != storage.end()) {
+        delete *(i->second);
+        index.erase(i->second);
+        storage.erase(i);
+        --entries_;
+        return true;
+    }
+    return false;
+}
+
+template <class EntryValue, size_t EntryCost>
+bool
+LruMap<EntryValue, EntryCost>::del(const char *key)
+{
+    LruMap::MapIterator i;
+    findEntry(key, i);
+    return del(i);
+}
+
+template <class EntryValue, size_t EntryCost>
+void
+LruMap<EntryValue, EntryCost>::trim()
+{
+    while(memLimit() > 0 && size() >= memLimit()) {
+        LruMap::QueueIterator i = index.end();
+        --i;
+        if (i != index.end()) {
+            del((*i)->key.c_str());
+        }
+    }
+}
+
+template <class EntryValue, size_t EntryCost>
+void
+LruMap<EntryValue, EntryCost>::touch(LruMap::MapIterator const &i)
+{
+    // this must not be done when nothing is being cached.
+    if (ttl == 0)
+        return;
+
+    index.push_front(*(i->second));
+    index.erase(i->second);
+    i->second = index.begin();
+}
+
+#endif

=== modified file 'src/Makefile.am'
--- src/Makefile.am	2012-11-02 23:54:44 +0000
+++ src/Makefile.am	2012-11-13 21:29:49 +0000
@@ -407,40 +407,41 @@
 	HttpRequest.cc \
 	HttpRequest.h \
 	HttpRequestMethod.cc \
 	HttpRequestMethod.h \
 	HttpVersion.h \
 	ICP.h \
 	icp_opcode.h \
 	icp_v2.cc \
 	icp_v3.cc \
 	int.h \
 	int.cc \
 	internal.h \
 	internal.cc \
 	$(IPC_SOURCE) \
 	ipcache.cc \
 	ipcache.h \
 	$(LEAKFINDERSOURCE) \
 	SquidList.h \
 	SquidList.cc \
 	lookup_t.h \
+	LruMap.h \
 	main.cc \
 	Mem.h \
 	mem.cc \
 	mem_node.cc \
 	mem_node.h \
 	Mem.h \
 	MemBuf.cc \
 	MemObject.cc \
 	MemObject.h \
 	mime.h \
 	mime.cc \
 	mime_header.h \
 	mime_header.cc \
 	multicast.h \
 	multicast.cc \
 	neighbors.h \
 	neighbors.cc \
 	Notes.cc \
 	Notes.h \
 	Packer.cc \

=== modified file 'src/cf.data.pre'
--- src/cf.data.pre	2012-11-24 14:27:56 +0000
+++ src/cf.data.pre	2012-11-24 14:28:11 +0000
@@ -2343,41 +2343,46 @@
 	Starting too few children temporary slows Squid under load while it
 	tries to spawn enough additional processes to cope with traffic.
 	
 		idle=N
 	
 	Sets a minimum of how many processes Squid is to try and keep available
 	at all times. When traffic begins to rise above what the existing
 	processes can handle this many more will be spawned up to the maximum
 	configured. A minimum setting of 1 is required.
 	
 	You must have at least one ssl_crtd process.
 DOC_END
 
 NAME: sslcrtvalidator_program
 TYPE: eol
 IFDEF: USE_SSL
 DEFAULT: none
 LOC: Ssl::TheConfig.ssl_crt_validator
 DOC_START
 	Specify the location and options of the executable for ssl_crt_validator
-	process.
+	process. Usage:
+		sslcrtvalidator_program [ttl=n] [cache=n] path ...
+
+	Options:
+	  ttl=n         TTL in seconds for cached results.The default is 60 secs
+	  cache=n       limit the result cache size. The default value is 2048
 DOC_END
 
 NAME: sslcrtvalidator_children
 TYPE: HelperChildConfig
 IFDEF: USE_SSL
 DEFAULT: 32 startup=5 idle=1 concurrency=1
 LOC: Ssl::TheConfig.ssl_crt_validator_Children
 DOC_START
 	The maximum number of processes spawn to service ssl server.
 	The maximum this may be safely set to is 32.
 	
 	The startup= and idle= options allow some measure of skew in your
 	tuning.
 	
 		startup=N
 	
 	Sets the minimum number of processes to spawn when Squid
 	starts or reconfigures. When set to zero the first request will
 	cause spawning of the first child process to handle it.
 	

=== modified file 'src/client_side.cc'
--- src/client_side.cc	2012-11-07 19:26:45 +0000
+++ src/client_side.cc	2012-11-13 21:29:49 +0000
@@ -3802,50 +3802,51 @@
             certProperties.signWithPkey.resetAndLock(port->signPkey.get());
     }
     signAlgorithm = certProperties.signAlgorithm;
 }
 
 void
 ConnStateData::getSslContextStart()
 {
     assert(areAllContextsForThisConnection());
     freeAllContexts();
     /* careful: freeAllContexts() above frees request, host, etc. */
 
     if (port->generateHostCertificates) {
         Ssl::CertificateProperties certProperties;
         buildSslCertGenerationParams(certProperties);
         sslBumpCertKey = certProperties.dbKey().c_str();
         assert(sslBumpCertKey.defined() && sslBumpCertKey[0] != '\0');
 
         debugs(33, 5, HERE << "Finding SSL certificate for " << sslBumpCertKey << " in cache");
         Ssl::LocalContextStorage & ssl_ctx_cache(Ssl::TheGlobalContextStorage.getLocalStorage(port->s));
-        SSL_CTX * dynCtx = ssl_ctx_cache.find(sslBumpCertKey.termedBuf());
-        if (dynCtx) {
+        SSL_CTX * dynCtx = NULL;
+        Ssl::SSL_CTX_Pointer *cachedCtx = ssl_ctx_cache.get(sslBumpCertKey.termedBuf());
+        if (cachedCtx && (dynCtx = cachedCtx->get())) {
             debugs(33, 5, HERE << "SSL certificate for " << sslBumpCertKey << " have found in cache");
             if (Ssl::verifySslCertificate(dynCtx, certProperties)) {
                 debugs(33, 5, HERE << "Cached SSL certificate for " << sslBumpCertKey << " is valid");
                 getSslContextDone(dynCtx);
                 return;
             } else {
                 debugs(33, 5, HERE << "Cached SSL certificate for " << sslBumpCertKey << " is out of date. Delete this certificate from cache");
-                ssl_ctx_cache.remove(sslBumpCertKey.termedBuf());
+                ssl_ctx_cache.del(sslBumpCertKey.termedBuf());
             }
         } else {
             debugs(33, 5, HERE << "SSL certificate for " << sslBumpCertKey << " haven't found in cache");
         }
 
 #if USE_SSL_CRTD
         try {
             debugs(33, 5, HERE << "Generating SSL certificate for " << certProperties.commonName << " using ssl_crtd.");
             Ssl::CrtdMessage request_message(Ssl::CrtdMessage::REQUEST);
             request_message.setCode(Ssl::CrtdMessage::code_new_certificate);
             request_message.composeRequest(certProperties);
             debugs(33, 5, HERE << "SSL crtd request: " << request_message.compose().c_str());
             Ssl::Helper::GetInstance()->sslSubmit(request_message, sslCrtdHandleReplyWrapper, this);
             return;
         } catch (const std::exception &e) {
             debugs(33, DBG_IMPORTANT, "ERROR: Failed to compose ssl_crtd " <<
                    "request for " << certProperties.commonName <<
                    " certificate: " << e.what() << "; will now block to " <<
                    "generate that certificate.");
             // fall through to do blocking in-process generation.
@@ -3856,41 +3857,41 @@
         dynCtx = Ssl::generateSslContext(certProperties, *port);
         getSslContextDone(dynCtx, true);
         return;
     }
     getSslContextDone(NULL);
 }
 
 void
 ConnStateData::getSslContextDone(SSL_CTX * sslContext, bool isNew)
 {
     // Try to add generated ssl context to storage.
     if (port->generateHostCertificates && isNew) {
 
         if (signAlgorithm == Ssl::algSignTrusted)
             Ssl::addChainToSslContext(sslContext, port->certsToChain.get());
         //else it is self-signed or untrusted do not attrach any certificate
 
         Ssl::LocalContextStorage & ssl_ctx_cache(Ssl::TheGlobalContextStorage.getLocalStorage(port->s));
         assert(sslBumpCertKey.defined() && sslBumpCertKey[0] != '\0');
         if (sslContext) {
-            if (!ssl_ctx_cache.add(sslBumpCertKey.termedBuf(), sslContext)) {
+            if (!ssl_ctx_cache.add(sslBumpCertKey.termedBuf(), new Ssl::SSL_CTX_Pointer(sslContext))) {
                 // If it is not in storage delete after using. Else storage deleted it.
                 fd_table[clientConnection->fd].dynamicSslContext = sslContext;
             }
         } else {
             debugs(33, 2, HERE << "Failed to generate SSL cert for " << sslConnectHostOrIp);
         }
     }
 
     // If generated ssl context = NULL, try to use static ssl context.
     if (!sslContext) {
         if (!port->staticSslContext) {
             debugs(83, DBG_IMPORTANT, "Closing SSL " << clientConnection->remote << " as lacking SSL context");
             clientConnection->close();
             return;
         } else {
             debugs(33, 5, HERE << "Using static ssl context.");
             sslContext = port->staticSslContext.get();
         }
     }
 

=== modified file 'src/forward.cc'
--- src/forward.cc	2012-11-24 14:12:30 +0000
+++ src/forward.cc	2012-11-24 14:31:46 +0000
@@ -730,173 +730,143 @@
         // remember the server certificate from the ErrorDetail object
         if (Ssl::ServerBump *serverBump = request->clientConnectionManager->serverBump()) {
             serverBump->serverCert.reset(SSL_get_peer_certificate(ssl));
 
             // remember validation errors, if any
             if (Ssl::Errors *errs = static_cast<Ssl::Errors *>(SSL_get_ex_data(ssl, ssl_ex_index_ssl_errors)))
                 serverBump->sslErrors = cbdataReference(errs);
         }
     }
 
     if (serverConnection()->getPeer() && !SSL_session_reused(ssl)) {
         if (serverConnection()->getPeer()->sslSession)
             SSL_SESSION_free(serverConnection()->getPeer()->sslSession);
 
         serverConnection()->getPeer()->sslSession = SSL_get1_session(ssl);
     }
 
 #if 1 // USE_SSL_CERT_VALIDATOR
     if (Ssl::TheConfig.ssl_crt_validator) {
         Ssl::CertValidationRequest validationRequest;
-        // WARNING: The STACK_OF(*) OpenSSL objects does not support locking.
-        // If we need to support locking we need to sk_X509_dup the STACK_OF(X509)
-        // list and lock all of the X509 members of the list.
-        // Currently we do not use any locking for any of the members of the
-        // Ssl::CertValidationRequest class. If the ssl object gone, the value returned
-        // from SSL_get_peer_cert_chain may not exist any more. In this code the
+        // WARNING: Currently we do not use any locking for any of the
+        // members of the Ssl::CertValidationRequest class. In this code the
         // Ssl::CertValidationRequest object used only to pass data to
         // Ssl::CertValidationHelper::submit method.
-        validationRequest.peerCerts = SSL_get_peer_cert_chain(ssl);
+        validationRequest.ssl = ssl;
         validationRequest.domainName = request->GetHost();
         if (Ssl::Errors *errs = static_cast<Ssl::Errors *>(SSL_get_ex_data(ssl, ssl_ex_index_ssl_errors)))
             // validationRequest disappears on return so no need to cbdataReference
             validationRequest.errors = errs;
         else
             validationRequest.errors = NULL;
         try {
             debugs(83, 5, "Sending SSL certificate for validation to ssl_crtvd.");
-            Ssl::CertValidationMsg requestMsg(Ssl::CrtdMessage::REQUEST);
-            requestMsg.setCode(Ssl::CertValidationMsg::code_cert_validate);
-            requestMsg.composeRequest(validationRequest);
-            debugs(83, 5, "SSL crtvd request: " << requestMsg.compose().c_str());
-            Ssl::CertValidationHelper::GetInstance()->sslSubmit(requestMsg, sslCrtvdHandleReplyWrapper, this);
+            Ssl::CertValidationHelper::GetInstance()->sslSubmit(validationRequest, sslCrtvdHandleReplyWrapper, this);
             return;
         } catch (const std::exception &e) {
             debugs(33, DBG_IMPORTANT, "ERROR: Failed to compose ssl_crtvd " <<
                    "request for " << validationRequest.domainName <<
                    " certificate: " << e.what() << "; will now block to " <<
                    "validate that certificate.");
             // fall through to do blocking in-process generation.
             ErrorState *anErr = new ErrorState(ERR_GATEWAY_FAILURE, HTTP_INTERNAL_SERVER_ERROR, request);
             fail(anErr);
             if (serverConnection()->getPeer()) {
                 peerConnectFailed(serverConnection()->getPeer());
             }
             serverConn->close();
             self = NULL;
             return;
         }
     }
 #endif // USE_SSL_CERT_VALIDATOR
 
     dispatch();
 }
 
 #if 1 // USE_SSL_CERT_VALIDATOR
 void
-FwdState::sslCrtvdHandleReplyWrapper(void *data, const HelperReply &reply)
+FwdState::sslCrtvdHandleReplyWrapper(void *data, Ssl::CertValidationResponse const &validationResponse)
 {
     FwdState * fwd = (FwdState *)(data);
-    fwd->sslCrtvdHandleReply(reply);
+    fwd->sslCrtvdHandleReply(validationResponse);
 }
 
 void
-FwdState::sslCrtvdHandleReply(const HelperReply &reply)
+FwdState::sslCrtvdHandleReply(Ssl::CertValidationResponse const &validationResponse)
 {
     Ssl::Errors *errs = NULL;
     Ssl::ErrorDetail *errDetails = NULL;
     bool validatorFailed = false;
     if (!Comm::IsConnOpen(serverConnection())) {
         return;
     }
-    SSL *ssl = fd_table[serverConnection()->fd].ssl;
 
-    if (!reply.other().hasContent()) {
-        debugs(83, DBG_IMPORTANT, "\"ssl_crtvd\" helper return <NULL> reply");
-        validatorFailed = true;
-    } else if (reply.result == HelperReply::BrokenHelper) {
-        debugs(83, DBG_IMPORTANT, "\"ssl_crtvd\" helper error response: " << reply.other().content());
+    debugs(83,5, request->GetHost() << " cert validation result: " << validationResponse.resultCode);
+
+    if (validationResponse.resultCode == HelperReply::Error)
+        errs = sslCrtvdCheckForErrors(validationResponse, errDetails);
+    else if (validationResponse.resultCode != HelperReply::Okay)
         validatorFailed = true;
-    } else  {
-        Ssl::CertValidationMsg replyMsg(Ssl::CrtdMessage::REPLY);
-        Ssl::CertValidationResponse validationResponse;
-        std::string error;
-        STACK_OF(X509) *peerCerts = SSL_get_peer_cert_chain(ssl);
-        if (replyMsg.parse(reply.other().content(), reply.other().contentSize()) != Ssl::CrtdMessage::OK ||
-                   !replyMsg.parseResponse(validationResponse, peerCerts, error) ) {
-            debugs(83, 5, "Reply from ssl_crtvd for " << request->GetHost() << " is incorrect");
-            validatorFailed = true;
-        } else {
-            if (reply.result == HelperReply::Okay) {
-                debugs(83, 5, "Certificate for " << request->GetHost() << " was successfully validated from ssl_crtvd");
-            } else if (reply.result == HelperReply::Error) {
-                debugs(83, 5, "Certificate for " << request->GetHost() << " found buggy by ssl_crtvd");
-                errs = sslCrtvdCheckForErrors(validationResponse, errDetails);
-            } else {
-                debugs(83, 5, "Certificate for " << request->GetHost() << " cannot be validated. ssl_crtvd response: " << replyMsg.getBody());
-                validatorFailed = true;
-            }
 
-            if (!errDetails && !validatorFailed) {
-                dispatch();
-                return;
-            }
-        }
+    if (!errDetails && !validatorFailed) {
+        dispatch();
+        return;
     }
 
     ErrorState *anErr = NULL;
     if (validatorFailed) {
         anErr = new ErrorState(ERR_GATEWAY_FAILURE, HTTP_INTERNAL_SERVER_ERROR, request);
     }  else {
 
         // Check the list error with
         if (errDetails && request->clientConnectionManager.valid()) {
             // remember the server certificate from the ErrorDetail object
             if (Ssl::ServerBump *serverBump = request->clientConnectionManager->serverBump()) {
                 // remember validation errors, if any
                 if (errs) {
                     if (serverBump->sslErrors)
                         cbdataReferenceDone(serverBump->sslErrors);
                     serverBump->sslErrors = cbdataReference(errs);
                 }
             }
         }
 
         anErr = makeConnectingError(ERR_SECURE_CONNECT_FAIL);
         anErr->detail = errDetails;
         /*anErr->xerrno= Should preserved*/
     }
 
     fail(anErr);
     if (serverConnection()->getPeer()) {
         peerConnectFailed(serverConnection()->getPeer());
     }
     serverConn->close();
     self = NULL;
     return;
 }
 
 /// Checks errors in the cert. validator response against sslproxy_cert_error.
 /// The first honored error, if any, is returned via errDetails parameter.
 /// The method returns all seen errors except SSL_ERROR_NONE as Ssl::Errors.
 Ssl::Errors *
-FwdState::sslCrtvdCheckForErrors(Ssl::CertValidationResponse &resp, Ssl::ErrorDetail *& errDetails)
+FwdState::sslCrtvdCheckForErrors(Ssl::CertValidationResponse const &resp, Ssl::ErrorDetail *& errDetails)
 {
     Ssl::Errors *errs = NULL;
 
     ACLFilledChecklist *check = NULL;
     if (acl_access *acl = Config.ssl_client.cert_error)
         check = new ACLFilledChecklist(acl, request, dash_str);
 
     SSL *ssl = fd_table[serverConnection()->fd].ssl;
     typedef Ssl::CertValidationResponse::RecvdErrors::const_iterator SVCRECI;
     for (SVCRECI i = resp.errors.begin(); i != resp.errors.end(); ++i) {
         debugs(83, 7, "Error item: " << i->error_no << " " << i->error_reason);
 
         assert(i->error_no != SSL_ERROR_NONE);
 
         if (!errDetails) {
             bool allowed = false;
             if (check) {
                 check->sslErrors = new Ssl::Errors(i->error_no);
                 if (check->fastCheck() == ACCESS_ALLOWED)
                     allowed = true;

=== modified file 'src/forward.h'
--- src/forward.h	2012-11-24 14:12:30 +0000
+++ src/forward.h	2012-11-24 14:28:11 +0000
@@ -69,45 +69,45 @@
     void serverClosed(int fd);
     void connectStart();
     void connectDone(const Comm::ConnectionPointer & conn, comm_err_t status, int xerrno);
     void connectTimeout(int fd);
     void initiateSSL();
     void negotiateSSL(int fd);
     bool checkRetry();
     bool checkRetriable();
     void dispatch();
     void pconnPush(Comm::ConnectionPointer & conn, const char *domain);
 
     bool dontRetry() { return flags.dont_retry; }
 
     void dontRetry(bool val) { flags.dont_retry = val; }
 
     /** return a ConnectionPointer to the current server connection (may or may not be open) */
     Comm::ConnectionPointer const & serverConnection() const { return serverConn; };
 
 #if USE_SSL //&& USE_SSL_CERT_VALIDATOR
     /// Callback function called when squid receive message from cert validator helper
-    static void sslCrtvdHandleReplyWrapper(void *data, const HelperReply &reply);
+    static void sslCrtvdHandleReplyWrapper(void *data, Ssl::CertValidationResponse const &);
     /// Process response from cert validator helper
-    void sslCrtvdHandleReply(const HelperReply &reply);
+    void sslCrtvdHandleReply(Ssl::CertValidationResponse const &);
     /// Check SSL errors returned from cert validator against sslproxy_cert_error access list
-    Ssl::Errors *sslCrtvdCheckForErrors(Ssl::CertValidationResponse &, Ssl::ErrorDetail *&);
+    Ssl::Errors *sslCrtvdCheckForErrors(Ssl::CertValidationResponse const &, Ssl::ErrorDetail *&);
 #endif
 private:
     // hidden for safer management of self; use static fwdStart
     FwdState(const Comm::ConnectionPointer &client, StoreEntry *, HttpRequest *, const AccessLogEntryPointer &alp);
     void start(Pointer aSelf);
 
 #if STRICT_ORIGINAL_DST
     void selectPeerForIntercepted();
 #endif
     static void logReplyStatus(int tries, http_status status);
     void doneWithRetries();
     void completed();
     void retryOrBail();
     ErrorState *makeConnectingError(const err_type type) const;
     static void RegisterWithCacheManager(void);
 
 public:
     StoreEntry *entry;
     HttpRequest *request;
     AccessLogEntryPointer al; ///< info for the future access.log entry

=== modified file 'src/ssl/cert_validate_message.cc'
--- src/ssl/cert_validate_message.cc	2012-11-24 14:27:56 +0000
+++ src/ssl/cert_validate_message.cc	2012-11-24 14:28:11 +0000
@@ -3,45 +3,46 @@
 #include "ssl/support.h"
 #include "ssl/cert_validate_message.h"
 #include "ssl/ErrorDetail.h"
 
 void
 Ssl::CertValidationMsg::composeRequest(CertValidationRequest const &vcert)
 {
     body.clear();
     body += Ssl::CertValidationMsg::param_host + "=" + vcert.domainName;
     if (vcert.errors) {
         body += "\n" + Ssl::CertValidationMsg::param_error + "=";
         bool comma = false;
         for (const Ssl::Errors *err = vcert.errors; err; err = err->next ) {
             if (comma)
                 body += ",";
             body += GetErrorName(err->element);
             comma = true;
         }
     }
 
-    if (vcert.peerCerts) {
+    STACK_OF(X509) *peerCerts = SSL_get_peer_cert_chain(vcert.ssl);
+    if (peerCerts) {
         body +="\n";
         Ssl::BIO_Pointer bio(BIO_new(BIO_s_mem()));
-        for (int i = 0; i < sk_X509_num(vcert.peerCerts); ++i) {
-            X509 *cert = sk_X509_value(vcert.peerCerts, i);
+        for (int i = 0; i < sk_X509_num(peerCerts); ++i) {
+            X509 *cert = sk_X509_value(peerCerts, i);
             PEM_write_bio_X509(bio.get(), cert);
             body = body + "cert_" + xitoa(i) + "=";
             char *ptr;
             long len = BIO_get_mem_data(bio.get(), &ptr);
             body.append(ptr, len);
             // Normally openssl toolkit terminates Certificate with a '\n'.
             if (ptr[len-1] != '\n')
                 body +="\n";
             if (!BIO_reset(bio.get())) {
                 // print an error?
             }
         }
     }
 }
 
 static int
 get_error_id(const char *label, size_t len)
 {
     const char *e = label + len -1;
     while (e != label && xisdigit(*e)) --e;

=== modified file 'src/ssl/cert_validate_message.h'
--- src/ssl/cert_validate_message.h	2012-11-24 14:27:56 +0000
+++ src/ssl/cert_validate_message.h	2012-11-24 14:28:11 +0000
@@ -1,76 +1,77 @@
 /*
  */
 
 #ifndef SQUID_SSL_CERT_VALIDATE_MESSAGE_H
 #define SQUID_SSL_CERT_VALIDATE_MESSAGE_H
 
 #include "ssl/support.h"
 #include "ssl/crtd_message.h"
 #include <vector>
 
 namespace Ssl
 {
 
 /**
  * This class is used to hold the required informations to build
  * a request message for the certificate validator helper
  */
 class CertValidationRequest
 {
 public:
-    STACK_OF(X509) *peerCerts; ///< The list of sent by SSL server
+    SSL *ssl;
     Errors *errors; ///< The list of errors detected
     std::string domainName; ///< The server name
-    CertValidationRequest() : peerCerts(NULL), errors(NULL) {}
+    CertValidationRequest() : ssl(NULL), errors(NULL) {}
 };
 
 /**
  * This class is used to store informations found in certificate validation
  * response messages read from certificate validator helper
  */
 class CertValidationResponse
 {
 public:
     /**
      * This class used to hold error informations returned from
      * cert validator helper.
      */
     class  RecvdError
     {
     public:
         RecvdError(): id(0), error_no(SSL_ERROR_NONE), cert(NULL) {}
         RecvdError(const RecvdError &);
         RecvdError & operator = (const RecvdError &);
         void setCert(X509 *);  ///< Sets cert to the given certificate
         int id; ///<  The id of the error
         ssl_error_t error_no; ///< The OpenSSL error code
         std::string error_reason; ///< A string describing the error
         X509_Pointer cert; ///< The broken certificate
     };
 
     typedef std::vector<RecvdError> RecvdErrors;
 
     /// Search in errors list for the error item with id=errorId.
     /// If none found a new RecvdError item added with the given id;
     RecvdError &getError(int errorId);
     RecvdErrors errors; ///< The list of parsed errors
+    HelperReply::Result_ resultCode; ///< The helper result code
 };
 
 /**
  * This class is responsible for composing or parsing messages destined to
  * or comming from a cert validator helper.
  * The messages format is:
  *   <response/request code> <whitespace> <body length> <whitespace> <key=value> ...\1
  */
 class CertValidationMsg: public CrtdMessage
 {
 private:
     /**
      * This class used to hold the certId/cert pairs found
      * in cert validation messages.
      */
     class CertItem
     {
     public:
         std::string name; ///< The certificate Id to use
         X509_Pointer cert;       ///< A pointer to certificate

=== modified file 'src/ssl/context_storage.cc'
--- src/ssl/context_storage.cc	2012-10-04 11:10:17 +0000
+++ src/ssl/context_storage.cc	2012-11-13 21:29:49 +0000
@@ -17,127 +17,50 @@
 Ssl::CertificateStorageAction::Pointer
 Ssl::CertificateStorageAction::Create(const Mgr::Command::Pointer &cmd)
 {
     return new CertificateStorageAction(cmd);
 }
 
 void Ssl::CertificateStorageAction::dump (StoreEntry *sentry)
 {
     StoreEntryStream stream(sentry);
     const char delimiter = '\t';
     const char endString = '\n';
     // Page title.
     stream << "Cached ssl certificates statistic.\n";
     // Title of statistic table.
     stream << "Port" << delimiter << "Max mem(KB)" << delimiter << "Cert number" << delimiter << "KB/cert" << delimiter << "Mem used(KB)" << delimiter << "Mem free(KB)" << endString;
 
     // Add info for each port.
     for (std::map<Ip::Address, LocalContextStorage *>::iterator i = TheGlobalContextStorage.storage.begin(); i != TheGlobalContextStorage.storage.end(); ++i) {
         stream << i->first << delimiter;
         LocalContextStorage & ssl_store_policy(*(i->second));
-        stream << ssl_store_policy.max_memory / 1024 << delimiter;
-        stream << ssl_store_policy.memory_used / SSL_CTX_SIZE << delimiter;
+        stream << ssl_store_policy.memLimit() / 1024 << delimiter;
+        stream << ssl_store_policy.entries() << delimiter;
         stream << SSL_CTX_SIZE / 1024 << delimiter;
-        stream << ssl_store_policy.memory_used / 1024 << delimiter;
-        stream << (ssl_store_policy.max_memory - ssl_store_policy.memory_used) / 1024 << endString;
+        stream << ssl_store_policy.size() / 1024 << delimiter;
+        stream << ssl_store_policy.freeMem() / 1024 << endString;
     }
     stream << endString;
     stream.flush();
 }
 
-Ssl::LocalContextStorage::LocalContextStorage(size_t aMax_memory)
-        :   max_memory(aMax_memory), memory_used(0)
-{}
-
-Ssl::LocalContextStorage::~LocalContextStorage()
-{
-    for (QueueIterator i = lru_queue.begin(); i != lru_queue.end(); ++i) {
-        delete *i;
-    }
-}
-
-SSL_CTX * Ssl::LocalContextStorage::add(const char * host_name, SSL_CTX * ssl_ctx)
-{
-    if (max_memory < SSL_CTX_SIZE) {
-        return NULL;
-    }
-    remove(host_name);
-    while (SSL_CTX_SIZE + memory_used > max_memory) {
-        purgeOne();
-    }
-    lru_queue.push_front(new Item(ssl_ctx, host_name));
-    storage.insert(MapPair(host_name, lru_queue.begin()));
-    memory_used += SSL_CTX_SIZE;
-    return ssl_ctx;
-}
-
-SSL_CTX * Ssl::LocalContextStorage::find(char const * host_name)
-{
-    MapIterator i = storage.find(host_name);
-    if (i == storage.end()) {
-        return NULL;
-    }
-    lru_queue.push_front(*(i->second));
-    lru_queue.erase(i->second);
-    i->second = lru_queue.begin();
-    return (*lru_queue.begin())->ssl_ctx;
-}
-
-void Ssl::LocalContextStorage::remove(char const * host_name)
-{
-    deleteAt(storage.find(host_name));
-}
-
-void Ssl::LocalContextStorage::purgeOne()
-{
-    QueueIterator i = lru_queue.end();
-    --i;
-    if (i != lru_queue.end()) {
-        remove((*i)->host_name.c_str());
-    }
-}
-
-void Ssl::LocalContextStorage::deleteAt(LocalContextStorage::MapIterator i)
-{
-    if (i != storage.end()) {
-
-        delete *(i->second);
-        lru_queue.erase(i->second);
-        storage.erase(i);
-        memory_used -= SSL_CTX_SIZE;
-    }
-}
-
-void Ssl::LocalContextStorage::SetSize(size_t aMax_memory)
-{
-    max_memory = aMax_memory;
-}
-
-Ssl::LocalContextStorage::Item::Item(SSL_CTX * aSsl_ctx, std::string const & aName)
-        :   ssl_ctx(aSsl_ctx), host_name(aName)
-{}
-
-Ssl::LocalContextStorage::Item::~Item()
-{
-    SSL_CTX_free(ssl_ctx);
-}
-
 ///////////////////////////////////////////////////////
 
 Ssl::GlobalContextStorage::GlobalContextStorage()
         :   reconfiguring(true)
 {
     RegisterAction("cached_ssl_cert", "Statistic of cached generated ssl certificates", &CertificateStorageAction::Create, 0, 1);
 }
 
 Ssl::GlobalContextStorage::~GlobalContextStorage()
 {
     for (std::map<Ip::Address, LocalContextStorage *>::iterator i = storage.begin(); i != storage.end(); ++i) {
         delete i->second;
     }
 }
 
 void Ssl::GlobalContextStorage::addLocalStorage(Ip::Address const & address, size_t size_of_store)
 {
     assert(reconfiguring);
     configureStorage.insert(std::pair<Ip::Address, size_t>(address, size_of_store));
 }
@@ -149,34 +72,34 @@
     assert (i != storage.end());
     return *(i->second);
 }
 
 void Ssl::GlobalContextStorage::reconfigureStart()
 {
     reconfiguring = true;
 }
 
 void Ssl::GlobalContextStorage::reconfigureFinish()
 {
     if (reconfiguring) {
         reconfiguring = false;
 
         // remove or change old local storages.
         for (std::map<Ip::Address, LocalContextStorage *>::iterator i = storage.begin(); i != storage.end(); ++i) {
             std::map<Ip::Address, size_t>::iterator conf_i = configureStorage.find(i->first);
             if (conf_i == configureStorage.end()) {
                 storage.erase(i);
             } else {
-                i->second->SetSize(conf_i->second);
+                i->second->setMemLimit(conf_i->second);
             }
         }
 
         // add new local storages.
         for (std::map<Ip::Address, size_t>::iterator conf_i = configureStorage.begin(); conf_i != configureStorage.end(); ++conf_i ) {
             if (storage.find(conf_i->first) == storage.end()) {
-                storage.insert(std::pair<Ip::Address, LocalContextStorage *>(conf_i->first, new LocalContextStorage(conf_i->second)));
+                storage.insert(std::pair<Ip::Address, LocalContextStorage *>(conf_i->first, new LocalContextStorage(-1, conf_i->second)));
             }
         }
     }
 }
 
 Ssl::GlobalContextStorage Ssl::TheGlobalContextStorage;

=== modified file 'src/ssl/context_storage.h'
--- src/ssl/context_storage.h	2012-10-04 11:10:17 +0000
+++ src/ssl/context_storage.h	2012-11-13 21:29:49 +0000
@@ -1,107 +1,63 @@
 #ifndef SQUID_SSL_CONTEXT_STORAGE_H
 #define SQUID_SSL_CONTEXT_STORAGE_H
 
 #if USE_SSL
 
 #include "SquidTime.h"
 #include "CacheManager.h"
 #include "ip/Address.h"
 #include "mgr/Action.h"
 #include "mgr/Command.h"
+#include "LruMap.h"
+#include "ssl/gadgets.h"
 #if HAVE_MAP
 #include <map>
 #endif
 #if HAVE_LIST
 #include <list>
 #endif
 #include <openssl/ssl.h>
 
 /// TODO: Replace on real size.
 #define SSL_CTX_SIZE 1024
 
 namespace  Ssl
 {
 
 /** Reports cached SSL certificate stats to Cache Manager.
  * TODO: Use "Report" functions instead friend class.
  */
 class CertificateStorageAction : public Mgr::Action
 {
 public:
     CertificateStorageAction(const Mgr::Command::Pointer &cmd);
     static Pointer Create(const Mgr::Command::Pointer &cmd);
     virtual void dump (StoreEntry *sentry);
     /**
      * We do not support aggregation of information across workers
      * TODO: aggregate these stats
      */
     virtual bool aggregatable() const { return false; }
 };
 
-/**
- * Memory cache for store generated SSL context. Enforces total size limits
- * using an LRU algorithm.
- */
-class LocalContextStorage
-{
-    friend class CertificateStorageAction;
-public:
-    /// Cache item is an (SSL_CTX, host name) tuple.
-    class Item
-    {
-    public:
-        Item(SSL_CTX * aSsl_ctx, std::string const & aName);
-        ~Item();
-    public:
-        SSL_CTX * ssl_ctx; ///< The SSL context.
-        std::string host_name; ///< The host name of the SSL context.
-    };
-
-    typedef std::list<Item *> Queue;
-    typedef Queue::iterator QueueIterator;
-
-    /// host_name:queue_item mapping for fast lookups by host name
-    typedef std::map<std::string, QueueIterator> Map;
-    typedef Map::iterator MapIterator;
-    typedef std::pair<std::string, QueueIterator> MapPair;
-
-    LocalContextStorage(size_t aMax_memory);
-    ~LocalContextStorage();
-    /// Set maximum memory size for this storage.
-    void SetSize(size_t aMax_memory);
-    /// Return a pointer to the  added ssl_ctx or NULL if fails (eg. max cache size equal 0).
-    SSL_CTX * add(char const * host_name, SSL_CTX * ssl_ctx);
-    /// Find SSL_CTX in storage by host name. Lru queue will be updated.
-    SSL_CTX * find(char const * host_name);
-    void remove(char const * host_name); ///< Delete the SSL context by hostname
-
-private:
-    void purgeOne(); ///< Delete oldest object.
-    /// Delete object by iterator. It is used in deletePurge() and remove(...) methods.
-    void deleteAt(MapIterator i);
-
-    size_t max_memory; ///< Max cache size.
-    size_t memory_used; ///< Used cache size.
-    Map storage; ///< The hostnames/SSL_CTX * pairs
-    Queue lru_queue; ///< LRU cache index
-};
+typedef LruMap<SSL_CTX_Pointer, SSL_CTX_SIZE> LocalContextStorage;
 
 /// Class for storing/manipulating LocalContextStorage per local listening address/port.
 class GlobalContextStorage
 {
     friend class CertificateStorageAction;
 public:
     GlobalContextStorage();
     ~GlobalContextStorage();
     /// Create new SSL context storage for the local listening address/port.
     void addLocalStorage(Ip::Address const & address, size_t size_of_store);
     /// Return the local storage for the given listening address/port.
     LocalContextStorage & getLocalStorage(Ip::Address const & address);
     /// When reconfigring should be called this method.
     void reconfigureStart();
 private:
     /// Called by getLocalStorage method
     void reconfigureFinish();
     bool reconfiguring; ///< True if system reconfiguring now.
     /// Storage used on configure or reconfigure.
     std::map<Ip::Address, size_t> configureStorage;

=== modified file 'src/ssl/helper.cc'
--- src/ssl/helper.cc	2012-11-24 14:12:30 +0000
+++ src/ssl/helper.cc	2012-11-24 14:54:54 +0000
@@ -1,30 +1,37 @@
 #include "squid.h"
 #include "anyp/PortCfg.h"
 #include "ssl/Config.h"
 #include "ssl/helper.h"
 #include "SquidString.h"
 #include "SquidTime.h"
 #include "SwapDir.h"
+#if 1 // USE_SSL_CERT_VALIDATOR
+#include "ssl/cert_validate_message.h"
+#endif
 #include "wordlist.h"
 #include "SquidConfig.h"
 
+#if 1 // USE_SSL_CERT_VALIDATOR
+LruMap<Ssl::CertValidationResponse> *Ssl::CertValidationHelper::HelperCache = NULL;
+#endif
+
 #if USE_SSL_CRTD
 Ssl::Helper * Ssl::Helper::GetInstance()
 {
     static Ssl::Helper sslHelper;
     return &sslHelper;
 }
 
 Ssl::Helper::Helper()
 {
 }
 
 Ssl::Helper::~Helper()
 {
     Shutdown();
 }
 
 void Ssl::Helper::Init()
 {
     assert(ssl_crtd == NULL);
 
@@ -130,65 +137,152 @@
 void Ssl::CertValidationHelper::Init()
 {
     assert(ssl_crt_validator == NULL);
 
     // we need to start ssl_crtd only if some port(s) need to bump SSL
     bool found = false;
     for (AnyP::PortCfg *s = ::Config.Sockaddr.http; !found && s; s = s->next)
         found = s->sslBump;
     for (AnyP::PortCfg *s = ::Config.Sockaddr.https; !found && s; s = s->next)
         found = s->sslBump;
     if (!found)
         return;
 
     ssl_crt_validator = new helper("ssl_crt_validator");
     ssl_crt_validator->childs.updateLimits(Ssl::TheConfig.ssl_crt_validator_Children);
     ssl_crt_validator->ipc_type = IPC_STREAM;
     // The crtd messages may contain the eol ('\n') character. We are
     // going to use the '\1' char as the end-of-message mark.
     ssl_crt_validator->eom = '\1';
     assert(ssl_crt_validator->cmdline == NULL);
+
+    int ttl = 60;
+    size_t cache = 2048;
     {
         char *tmp = xstrdup(Ssl::TheConfig.ssl_crt_validator);
         char *tmp_begin = tmp;
         char * token = NULL;
+        bool parseParams = true;
         while ((token = strwordtok(NULL, &tmp))) {
+            if (parseParams) {
+                if (strncmp(token, "ttl=", 4) == 0) {
+                    ttl = atoi(token + 4);
+                    continue;
+                } else if (strncmp(token, "cache=", 6) == 0) {
+                    cache = atoi(token + 6);
+                    continue;
+                } else
+                    parseParams = false;
+            }
             wordlistAdd(&ssl_crt_validator->cmdline, token);
         }
         xfree(tmp_begin);
     }
     helperOpenServers(ssl_crt_validator);
+
+    //WARNING: initializing static member in an object initialization method
+    assert(HelperCache == NULL);
+    HelperCache = new LruMap<Ssl::CertValidationResponse>(ttl, cache);
 }
 
 void Ssl::CertValidationHelper::Shutdown()
 {
     if (!ssl_crt_validator)
         return;
     helperShutdown(ssl_crt_validator);
     wordlistDestroy(&ssl_crt_validator->cmdline);
     delete ssl_crt_validator;
     ssl_crt_validator = NULL;
+    
+    // CertValidationHelper::HelperCache is a static member, it is not good policy to
+    // reset it here. Will work because the current Ssl::CertValidationHelper is 
+    // always the same static object.
+    delete HelperCache;
+    HelperCache = NULL;
+}
+
+struct submitData {
+    std::string query;
+    Ssl::CertValidationHelper::CVHCB *callback;
+    void *data;
+    SSL *ssl;
+    CBDATA_CLASS2(submitData);
+};
+CBDATA_CLASS_INIT(submitData);
+
+static void
+sslCrtvdHandleReplyWrapper(void *data, const HelperReply &reply)
+{
+    Ssl::CertValidationMsg replyMsg(Ssl::CrtdMessage::REPLY);
+    Ssl::CertValidationResponse *validationResponse = new Ssl::CertValidationResponse;
+    std::string error;
+
+    submitData *crtdvdData = static_cast<submitData *>(data);
+    STACK_OF(X509) *peerCerts = SSL_get_peer_cert_chain(crtdvdData->ssl);
+    if (reply.result == HelperReply::BrokenHelper) {
+        debugs(83, DBG_IMPORTANT, "\"ssl_crtvd\" helper error response: " << reply.other().content());
+        validationResponse->resultCode = HelperReply::BrokenHelper;
+    } else if (replyMsg.parse(reply.other().content(), reply.other().contentSize()) != Ssl::CrtdMessage::OK ||
+        !replyMsg.parseResponse(*validationResponse, peerCerts, error) ) {
+        debugs(83, DBG_IMPORTANT, "WARNING: Reply from ssl_crtvd for " << " is incorrect");
+        debugs(83, DBG_IMPORTANT, "Certificate cannot be validated. ssl_crtvd response: " << replyMsg.getBody());
+        validationResponse->resultCode = HelperReply::BrokenHelper;
+    }
+    else
+        validationResponse->resultCode = reply.result;
+
+    crtdvdData->callback(crtdvdData->data, *validationResponse);
+
+    if (Ssl::CertValidationHelper::HelperCache &&
+        (validationResponse->resultCode == HelperReply::Okay || validationResponse->resultCode == HelperReply::Error)) {
+        Ssl::CertValidationHelper::HelperCache->add(crtdvdData->query.c_str(), validationResponse);
+    } else
+        delete validationResponse;
+
+    cbdataReferenceDone(crtdvdData->data);
+    SSL_free(crtdvdData->ssl);
+    delete crtdvdData;
 }
 
-void Ssl::CertValidationHelper::sslSubmit(CrtdMessage const & message, HLPCB * callback, void * data)
+void Ssl::CertValidationHelper::sslSubmit(Ssl::CertValidationRequest const &request, Ssl::CertValidationHelper::CVHCB * callback, void * data)
 {
     static time_t first_warn = 0;
     assert(ssl_crt_validator);
 
     if (ssl_crt_validator->stats.queue_size >= (int)(ssl_crt_validator->childs.n_running * 2)) {
         if (first_warn == 0)
             first_warn = squid_curtime;
         if (squid_curtime - first_warn > 3 * 60)
             fatal("ssl_crtvd queue being overloaded for long time");
         debugs(83, DBG_IMPORTANT, "WARNING: ssl_crtvd queue overload, rejecting");
-        const char *errMsg = "BH error 45 Temporary network problem, please retry later";
-        HelperReply failReply(errMsg,strlen(errMsg));
-        callback(data, failReply);
+        Ssl::CertValidationResponse resp;
+        resp.resultCode = HelperReply::BrokenHelper;
+        callback(data, resp);
         return;
     }
-
     first_warn = 0;
-    std::string msg = message.compose();
-    msg += '\n';
-    helperSubmit(ssl_crt_validator, msg.c_str(), callback, data);
+
+    Ssl::CertValidationMsg message(Ssl::CrtdMessage::REQUEST);
+    message.setCode(Ssl::CertValidationMsg::code_cert_validate);
+    message.composeRequest(request);
+    debugs(83, 5, "SSL crtvd request: " << message.compose().c_str());
+
+    submitData *crtdvdData = new submitData;
+    crtdvdData->query = message.compose();
+    crtdvdData->query += '\n';
+    crtdvdData->callback = callback;
+    crtdvdData->data = cbdataReference(data);
+    crtdvdData->ssl = request.ssl;
+    CRYPTO_add(&crtdvdData->ssl->references,1,CRYPTO_LOCK_SSL);
+    Ssl::CertValidationResponse const*validationResponse;
+
+    if (CertValidationHelper::HelperCache &&
+        (validationResponse = CertValidationHelper::HelperCache->get(crtdvdData->query.c_str()))) {
+        callback(data, *validationResponse);
+        cbdataReferenceDone(crtdvdData->data);
+        SSL_free(crtdvdData->ssl);
+        delete crtdvdData;
+        return;
+    }
+    helperSubmit(ssl_crt_validator, crtdvdData->query.c_str(), sslCrtvdHandleReplyWrapper, crtdvdData);
 }
 #endif // USE_SSL_CERT_VALIDATOR

=== modified file 'src/ssl/helper.h'
--- src/ssl/helper.h	2012-11-24 14:12:30 +0000
+++ src/ssl/helper.h	2012-11-24 14:58:04 +0000
@@ -1,49 +1,58 @@
 #ifndef SQUID_SSL_HELPER_H
 #define SQUID_SSL_HELPER_H
 
 #include "../helper.h"
+#include "LruMap.h"
+#if 1 // USE_SSL_CERT_VALIDATOR
+#include "ssl/cert_validate_message.h"
+#endif
 #include "ssl/crtd_message.h"
 
 namespace Ssl
 {
 /**
  * Set of thread for ssl_crtd. This class is singleton. Use this class only
  * over GetIntance() static method. This class use helper structure
  * for threads management.
  */
 #if USE_SSL_CRTD
 class Helper
 {
 public:
     static Helper * GetInstance(); ///< Instance class.
     void Init(); ///< Init helper structure.
     void Shutdown(); ///< Shutdown helper structure.
     /// Submit crtd message to external crtd server.
     void sslSubmit(CrtdMessage const & message, HLPCB * callback, void *data);
 private:
     Helper();
     ~Helper();
 
     helper * ssl_crtd; ///< helper for management of ssl_crtd.
 };
 #endif
 
 #if 1 // USE_SSL_CERT_VALIDATOR
+class CertValidationRequest;
+class CertValidationResponse;
 class CertValidationHelper
 {
 public:
+    typedef void CVHCB(void *, Ssl::CertValidationResponse const &);
     static CertValidationHelper * GetInstance(); ///< Instance class.
     void Init(); ///< Init helper structure.
     void Shutdown(); ///< Shutdown helper structure.
-    /// Submit crtd message to external crtd server.
-    void sslSubmit(CrtdMessage const & message, HLPCB * callback, void *data);
+    /// Submit crtd request message to external crtd server.
+    void sslSubmit(Ssl::CertValidationRequest const & request, CVHCB * callback, void *data);
 private:
     CertValidationHelper();
     ~CertValidationHelper();
 
     helper * ssl_crt_validator; ///< helper for management of ssl_crtd.
+public:
+    static LruMap<Ssl::CertValidationResponse> *HelperCache; ///< cache for cert validation helper
 };
 #endif // USE_SSL_CERT_VALIDATOR
 
 } //namespace Ssl
 #endif // SQUID_SSL_HELPER_H

=== modified file 'src/tests/stub_libsslsquid.cc'
--- src/tests/stub_libsslsquid.cc	2012-09-20 16:26:47 +0000
+++ src/tests/stub_libsslsquid.cc	2012-10-24 18:18:55 +0000
@@ -1,53 +1,43 @@
 #include "squid.h"
 
 #if USE_SSL
 
 #include "fatal.h"
 
 /* Stub File for the ssl/libsslsquid.la convenience library */
 
 #define STUB_API "ssl/libsslsquid.la"
 #include "tests/STUB.h"
 
 #include "ssl/Config.h"
 Ssl::Config::Config() { printf("Ssl::Config::Config No implemented\n"); }
 Ssl::Config::~Config() { printf("Ssl::Config::Config No implemented\n"); }
 Ssl::Config Ssl::TheConfig;
 
 #include "ssl/context_storage.h"
 //Ssl::CertificateStorageAction::CertificateStorageAction(const Mgr::Command::Pointer &cmd) STUB
 Ssl::CertificateStorageAction::Pointer Ssl::CertificateStorageAction::Create(const Mgr::Command::Pointer &cmd) STUB_RETSTATREF(Ssl::CertificateStorageAction::Pointer)
 void Ssl::CertificateStorageAction::dump(StoreEntry *sentry) STUB
-Ssl::LocalContextStorage::Item::Item(SSL_CTX * aSsl_ctx, std::string const & aName) STUB
-Ssl::LocalContextStorage::Item::~Item() STUB
-Ssl::LocalContextStorage::LocalContextStorage(size_t aMax_memory) STUB
-Ssl::LocalContextStorage::~LocalContextStorage() STUB
-void Ssl::LocalContextStorage::SetSize(size_t aMax_memory) STUB
-SSL_CTX * Ssl::LocalContextStorage::add(char const * host_name, SSL_CTX * ssl_ctx) STUB_RETVAL(NULL)
-SSL_CTX * Ssl::LocalContextStorage::find(char const * host_name) STUB_RETVAL(NULL)
-void Ssl::LocalContextStorage::remove(char const * host_name) STUB
-//Ssl::GlobalContextStorage::GlobalContextStorage() STUB
-//Ssl::GlobalContextStorage::~GlobalContextStorage() STUB
 void Ssl::GlobalContextStorage::addLocalStorage(Ip::Address const & address, size_t size_of_store) STUB
 Ssl::LocalContextStorage & Ssl::GlobalContextStorage::getLocalStorage(Ip::Address const & address)
-{ fatal(STUB_API " required"); static Ssl::LocalContextStorage v(0); return v; }
+{ fatal(STUB_API " required"); static Ssl::LocalContextStorage v(0,0); return v; }
 void Ssl::GlobalContextStorage::reconfigureStart() STUB
 //Ssl::GlobalContextStorage Ssl::TheGlobalContextStorage;
 
 #include "ssl/ErrorDetail.h"
 Ssl::ssl_error_t parseErrorString(const char *name) STUB_RETVAL(0)
 //const char *Ssl::getErrorName(ssl_error_t value) STUB_RETVAL(NULL)
 Ssl::ErrorDetail::ErrorDetail(ssl_error_t err_no, X509 *, X509 *, const char *) STUB
 Ssl::ErrorDetail::ErrorDetail(ErrorDetail const &) STUB
 const String & Ssl::ErrorDetail::toString() const STUB_RETSTATREF(String)
 
 #include "ssl/support.h"
 SSL_CTX *sslCreateServerContext(AnyP::PortCfg &) STUB_RETVAL(NULL)
 SSL_CTX *sslCreateClientContext(const char *certfile, const char *keyfile, int version, const char *cipher, const char *options, const char *flags, const char *CAfile, const char *CApath, const char *CRLfile) STUB_RETVAL(NULL)
 int ssl_read_method(int, char *, int) STUB_RETVAL(0)
 int ssl_write_method(int, const char *, int) STUB_RETVAL(0)
 void ssl_shutdown_method(SSL *) STUB
 const char *sslGetUserEmail(SSL *ssl) STUB_RETVAL(NULL)
 // typedef char const *SSLGETATTRIBUTE(SSL *, const char *);
 // SSLGETATTRIBUTE sslGetUserAttribute;
 // SSLGETATTRIBUTE sslGetCAAttribute;

Reply via email to