This patch uses the the "--long-options" ACLs feature which posted to squid-dev under the mailthread:
 "PATCH] Adds support for --long-acl-options"


Patch description:

Many popular servers use certificates with several "alternative subject names" (SubjectAltName). Many of those names are wildcards. For example, a www.youtube.com certificate currently includes *.google.com and 50+ other subject names, most of which are wildcards.

Often, admins want server_name to match any of the subject names. This is useful to match any server belonging to a large conglomerate of companies, all including some *.example.com name in their certificates. The existing server_name functionality addresses this use case well.

The new ACL options address several other important use cases:

--consensus allows matching a part of the conglomerate when the part's subject name is included in certificates used by many other conglomerate parts (e.g., matching Google but not Youtube).

--client-requested allows both (a) SNI-based matching even after Squid obtains the server certificate and (b) pinpointing a particular server in a group of different servers all using the same wildcard certificate (e.g., matching appengine.example.com but not www.example.com when the certificate for has *.example.com subject).

--server-provided allows matching only after Squid obtains the server certificate and matches any of the conglomerate parts.

Also this patch fixes squid to log client SNI when client-first bumping mode is used too.

This is a Measurement Factory project
ssl::server_name options to control matching logic.

Many popular servers use certificates with several "alternative subject
names" (SubjectAltName). Many of those names are wildcards. For example,
a www.youtube.com certificate currently includes *.google.com and 50+
other subject names, most of which are wildcards.

Often, admins want server_name to match any of the subject names. This
is useful to match any server belonging to a large conglomerate of
companies, all including some *.example.com name in their certificates.
The existing server_name functionality addresses this use case well.

The new ACL options address several other important use cases:

--consensus allows matching a part of the conglomerate when the part's
  subject name is included in certificates used by many other conglomerate
  parts (e.g., matching Google but not Youtube).

--client-requested allows both (a) SNI-based matching even after
  Squid obtains the server certificate and (b) pinpointing a particular
  server in a group of different servers all using the same wildcard
  certificate (e.g., matching appengine.example.com but not
  www.example.com when the certificate for has *.example.com subject).

--server-provided allows matching only after Squid obtains the server
  certificate and matches any of the conglomerate parts.

Also this patch fixes squid to log client SNI when client-first bumping mode
is used too.

This is a Measurement Factory project.

=== modified file 'src/acl/ServerName.cc'
--- src/acl/ServerName.cc	2017-05-14 18:19:54 +0000
+++ src/acl/ServerName.cc	2017-05-26 08:52:11 +0000
@@ -75,44 +75,89 @@
     char *s = reinterpret_cast<char *>(cn_data->data);
     char *d = cn;
     for (int i = 0; i < cn_data->length; ++i, ++d, ++s) {
         if (*s == '\0')
             return 1; // always a domain mismatch. contains 0x00
         *d = *s;
     }
     cn[cn_data->length] = '\0';
     debugs(28, 4, "Verifying certificate name/subjectAltName " << cn);
     if (data->match(cn))
         return 0;
     return 1;
 }
 
 int
 ACLServerNameStrategy::match (ACLData<MatchType> * &data, ACLFilledChecklist *checklist)
 {
     assert(checklist != NULL && checklist->request != NULL);
 
     const char *serverName = nullptr;
-    SBuf serverNameKeeper; // because c_str() is not constant
+    SBuf clientSniKeeper; // because c_str() is not constant
     if (ConnStateData *conn = checklist->conn()) {
-
-        if (conn->serverBump()) {
-            if (X509 *peer_cert = conn->serverBump()->serverCert.get())
-                return Ssl::matchX509CommonNames(peer_cert, (void *)data, check_cert_domain<MatchType>);
-        }
-
-        if (conn->sslCommonName().isEmpty()) {
+        const char *clientRequestedServerName = nullptr;
+        clientSniKeeper = conn->tlsClientSni();
+        if (clientSniKeeper.isEmpty()) {
             const char *host = checklist->request->url.host();
             if (host && *host) // paranoid first condition: host() is never nil
-                serverName = host;
-        } else {
-            serverNameKeeper = conn->sslCommonName();
-            serverName = serverNameKeeper.c_str();
+                clientRequestedServerName = host;
+        } else
+            clientRequestedServerName = clientSniKeeper.c_str();
+
+        if (useConsensus) {
+            X509 *peer_cert = conn->serverBump() ? conn->serverBump()->serverCert.get() : nullptr;
+            // use the client requested name if it matches the server
+            // certificate or if the certificate is not available
+            if (!peer_cert || Ssl::checkX509ServerValidity(peer_cert, clientRequestedServerName))
+                serverName = clientRequestedServerName;
+        } else if (useClientRequested)
+            serverName = clientRequestedServerName;
+        else { // either no options or useServerProvided
+            if (X509 *peer_cert = (conn->serverBump() ? conn->serverBump()->serverCert.get() : nullptr))
+                return Ssl::matchX509CommonNames(peer_cert, (void *)data, check_cert_domain<MatchType>);
+            if (!useServerProvided)
+                serverName = clientRequestedServerName;
         }
     }
 
     if (!serverName)
         serverName = "none";
 
     return data->match(serverName);
 }
 
+const Acl::Options &
+ACLServerNameStrategy::options()
+{
+    static const Acl::BooleanOption ClientRequested;
+    static const Acl::BooleanOption ServerProvided;
+    static const Acl::BooleanOption Consensus;
+    static const Acl::Options MyOptions = {
+        {"--client-requested", &ClientRequested},
+        {"--server-provided", &ServerProvided},
+        {"--consensus", &Consensus}
+    };
+
+    ClientRequested.linkWith(&useClientRequested);
+    ServerProvided.linkWith(&useServerProvided);
+    Consensus.linkWith(&useConsensus);
+    return MyOptions;
+}
+
+bool
+ACLServerNameStrategy::valid() const
+{
+    int optionCount = 0;
+
+    if (useClientRequested)
+        optionCount++;
+    if (useServerProvided)
+        optionCount++;
+    if (useConsensus)
+        optionCount++;
+
+    if (optionCount > 1) {
+        debugs(28, DBG_CRITICAL, "ERROR: Multiple options given for the server_name ACL");
+        return false;
+    }
+    return true;
+}

=== modified file 'src/acl/ServerName.h'
--- src/acl/ServerName.h	2017-05-14 18:19:54 +0000
+++ src/acl/ServerName.h	2017-05-26 08:51:21 +0000
@@ -11,25 +11,31 @@
 
 #include "acl/Acl.h"
 #include "acl/DomainData.h"
 #include "acl/Strategy.h"
 
 class ACLServerNameData : public ACLDomainData {
     MEMPROXY_CLASS(ACLServerNameData);
 public:
     ACLServerNameData() : ACLDomainData() {}
     virtual bool match(const char *);
     virtual ACLData<char const *> *clone() const;
 };
 
 class ACLServerNameStrategy : public ACLStrategy<char const *>
 {
 
 public:
     /* ACLStrategy API */
     virtual int match (ACLData<MatchType> * &, ACLFilledChecklist *);
     virtual bool requiresRequest() const {return true;}
+    virtual const Acl::Options &options();
+    virtual bool valid() const;
 
+private:
+    Acl::BooleanOptionValue useClientRequested; ///< Ignore server-supplied names?
+    Acl::BooleanOptionValue useServerProvided; ///< Ignore client-supplied names?
+    Acl::BooleanOptionValue useConsensus; ///< Ignore mismatching names?
 };
 
 #endif /* SQUID_ACLSERVERNAME_H */
 

=== modified file 'src/cf.data.pre'
--- src/cf.data.pre	2017-05-24 08:24:11 +0000
+++ src/cf.data.pre	2017-05-26 10:01:53 +0000
@@ -1324,51 +1324,81 @@
 	acl aclname server_cert_fingerprint [-sha1] fingerprint
 	  # match against server SSL certificate fingerprint [fast]
 	  #
 	  # The fingerprint is the digest of the DER encoded version 
 	  # of the whole certificate. The user should use the form: XX:XX:...
 	  # Optional argument specifies the digest algorithm to use.
 	  # The SHA1 digest algorithm is the default and is currently
 	  # the only algorithm supported (-sha1).
 
 	acl aclname at_step step
 	  # match against the current step during ssl_bump evaluation [fast]
 	  # Never matches and should not be used outside the ssl_bump context.
 	  #
 	  # At each SslBump step, Squid evaluates ssl_bump directives to find
 	  # the next bumping action (e.g., peek or splice). Valid SslBump step
 	  # values and the corresponding ssl_bump evaluation moments are:
 	  #   SslBump1: After getting TCP-level and HTTP CONNECT info.
 	  #   SslBump2: After getting SSL Client Hello info.
 	  #   SslBump3: After getting SSL Server Hello info.
 
-	acl aclname ssl::server_name .foo.com ...
+	acl aclname ssl::server_name [option] .foo.com ...
 	  # matches server name obtained from various sources [fast]
 	  #
-	  # The server name is obtained during Ssl-Bump steps from such sources
-	  # as CONNECT request URI, client SNI, and SSL server certificate CN.
-	  # During each Ssl-Bump step, Squid may improve its understanding of a
-	  # "true server name". Unlike dstdomain, this ACL does not perform
-	  # DNS lookups.
-	  # The "none" name can be used to match transactions where Squid
+	  # The ACL computes server name(s) using such information sources as
+	  # CONNECT request URI, TLS client SNI, and TLS server certificate 
+	  # subject (CN and SubjectAltName). The computed server name(s) usually
+	  # change with each SslBump step, as more info becomes available:
+	  # * SNI is used as the server name instead of the request URI,
+	  # * subject name(s) from the server certificate (CN and
+	  #   SubjectAltName) are used as the server names instead of SNI.
+	  #
+	  # When the ACL computes multiple server names, matching any single
+	  # computed name is sufficient for the ACL to match.
+	  #
+	  # The "none" name can be used to match transactions where the ACL
 	  # could not compute the server name using any information source
-	  # already available at the ACL evaluation time.
+	  # that was both available and allowed to be used by the ACL options at
+	  # the ACL evaluation time.
+	  #
+	  # Unlike dstdomain, this ACL does not perform DNS lookups.
+	  #
+	  # An ACL option below may be used to restrict what information 
+	  # sources are used to extract the server names from:
+	  #
+	  # --client-requested
+	  #   The server name is SNI regardless of what the server says.
+	  # --server-provided
+	  #   The server name(s) are the certificate subject name(s), regardless
+	  #   of what the client has requested. If the server certificate is
+	  #   unavailable, then the name is "none".
+	  # --consensus
+	  #   The server name is either SNI (if SNI matches at least one of the
+	  #   certificate subject names) or "none" (otherwise). When the server
+	  #   certificate is unavailable, the consensus server name is SNI.
+	  #
+	  # Combining multiple options in one ACL is a fatal configuration
+	  # error.
+	  #
+	  # For all options: If no SNI is available, then the CONNECT request
+	  # target (a.k.a. Host header or URI) is used instead of SNI (for an
+	  # intercepted connection, this target is the destination IP address).
 
 	acl aclname ssl::server_name_regex [-i] \.foo\.com ...
 	  # regex matches server name obtained from various sources [fast]
 
 	acl aclname connections_encrypted
 	  # matches transactions with all HTTP messages received over TLS
 	  # transport connections. [fast]
 	  #
 	  # The master transaction deals with HTTP messages received from
 	  # various sources. All sources used by the master transaction in the
 	  # past are considered by the ACL. The following rules define whether
 	  # a given message source taints the entire master transaction,
 	  # resulting in ACL mismatches:
 	  #
 	  #  * The HTTP client transport connection is not TLS.
 	  #  * An adaptation service connection-encryption flag is off.
 	  #  * The peer or origin server transport connection is not TLS.
 	  #
 	  # Caching currently does not affect these rules. This cache ignorance
 	  # implies that only the current HTTP client transport and REQMOD
@@ -4403,43 +4433,41 @@
 		Sh	Squid hierarchy status (DEFAULT_PARENT etc)
 
 	SSL-related format codes:
 
 		ssl::bump_mode	SslBump decision for the transaction:
 
 				For CONNECT requests that initiated bumping of
 				a connection and for any request received on
 				an already bumped connection, Squid logs the
 				corresponding SslBump mode ("server-first" or
 				"client-first"). See the ssl_bump option for
 				more information about these modes.
 
 				A "none" token is logged for requests that
 				triggered "ssl_bump" ACL evaluation matching
 				either a "none" rule or no rules at all.
 
 				In all other cases, a single dash ("-") is
 				logged.
 
-		ssl::>sni	SSL client SNI sent to Squid. Available only
-				after the peek, stare, or splice SSL bumping
-				actions.
+		ssl::>sni	SSL client SNI sent to Squid.
 
 		ssl::>cert_subject
 				The Subject field of the received client
 				SSL certificate or a dash ('-') if Squid has
 				received an invalid/malformed certificate or
 				no certificate at all. Consider encoding the
 				logged value because Subject often has spaces.
 
 		ssl::>cert_issuer
 				The Issuer field of the received client
 				SSL certificate or a dash ('-') if Squid has
 				received an invalid/malformed certificate or
 				no certificate at all. Consider encoding the
 				logged value because Issuer often has spaces.
 
 		ssl::<cert_errors
 				The list of certificate validation errors
 				detected by Squid (including OpenSSL and
 				certificate validation helper components). The
 				errors are listed in the discovery order. By

=== modified file 'src/client_side.cc'
--- src/client_side.cc	2017-03-02 01:26:30 +0000
+++ src/client_side.cc	2017-05-25 08:47:53 +0000
@@ -3138,42 +3138,41 @@
             readSomeData();
             return;
         }
     }
     catch (const std::exception &ex) {
         debugs(83, 2, "error on FD " << clientConnection->fd << ": " << ex.what());
         unsupportedProtocol = true;
     }
 
     parsingTlsHandshake = false;
 
     if (mayTunnelUnsupportedProto())
         preservedClientData = inBuf;
 
     // Even if the parser failed, each TLS detail should either be set
     // correctly or still be "unknown"; copying unknown detail is a no-op.
     Security::TlsDetails::Pointer const &details = tlsParser.details;
     clientConnection->tlsNegotiations()->retrieveParsedInfo(details);
     if (details && !details->serverName.isEmpty()) {
         resetSslCommonName(details->serverName.c_str());
-        if (sslServerBump)
-            sslServerBump->clientSni = details->serverName;
+        tlsClientSni_ = details->serverName;
     }
 
     // We should disable read/write handlers
     Comm::SetSelect(clientConnection->fd, COMM_SELECT_READ, NULL, NULL, 0);
     Comm::SetSelect(clientConnection->fd, COMM_SELECT_WRITE, NULL, NULL, 0);
 
     if (unsupportedProtocol) {
         Http::StreamPointer context = pipeline.front();
         Must(context && context->http);
         HttpRequest::Pointer request = context->http->request;
         debugs(83, 5, "Got something other than TLS Client Hello. Cannot SslBump.");
         sslBumpMode = Ssl::bumpNone;
         if (!clientTunnelOnError(this, context, request, HttpRequestMethod(), ERR_PROTOCOL_UNKNOWN))
             clientConnection->close();
         return;
     }
 
     if (!sslServerBump || sslServerBump->act.step1 == Ssl::bumpClientFirst) { // Either means client-first.
         getSslContextStart();
         return;
@@ -3353,42 +3352,42 @@
     debugs(33, 2, "Request tunneling for " << reason);
     ClientHttpRequest *http = buildFakeRequest(method, connectHost, connectPort, payload);
     HttpRequest::Pointer request = http->request;
     request->flags.forceTunnel = true;
     http->calloutContext = new ClientRequestContext(http);
     http->doCallouts();
     clientProcessRequestFinished(this, request);
     return true;
 }
 
 bool
 ConnStateData::fakeAConnectRequest(const char *reason, const SBuf &payload)
 {
     debugs(33, 2, "fake a CONNECT request to force connState to tunnel for " << reason);
 
     SBuf connectHost;
     assert(transparent());
     const unsigned short connectPort = clientConnection->local.port();
 
 #if USE_OPENSSL
-    if (serverBump() && !serverBump()->clientSni.isEmpty())
-        connectHost.assign(serverBump()->clientSni);
+    if (!tlsClientSni_.isEmpty())
+        connectHost.assign(tlsClientSni_);
     else
 #endif
     {
         static char ip[MAX_IPSTRLEN];
         connectHost.assign(clientConnection->local.toStr(ip, sizeof(ip)));
     }
 
     ClientHttpRequest *http = buildFakeRequest(Http::METHOD_CONNECT, connectHost, connectPort, payload);
 
     http->calloutContext = new ClientRequestContext(http);
     HttpRequest::Pointer request = http->request;
     http->doCallouts();
     clientProcessRequestFinished(this, request);
     return true;
 }
 
 ClientHttpRequest *
 ConnStateData::buildFakeRequest(Http::MethodType const method, SBuf &useHost, unsigned short usePort, const SBuf &payload)
 {
     ClientHttpRequest *http = new ClientHttpRequest(this);

=== modified file 'src/client_side.h'
--- src/client_side.h	2017-03-05 06:46:20 +0000
+++ src/client_side.h	2017-05-26 09:34:52 +0000
@@ -227,40 +227,41 @@
      * \param[in] isNew if generated certificate is new, so we need to add this certificate to storage.
      */
     void getSslContextDone(Security::ContextPointer &, bool isNew = false);
     /// Callback function. It is called when squid receive message from ssl_crtd.
     static void sslCrtdHandleReplyWrapper(void *data, const Helper::Reply &reply);
     /// Proccess response from ssl_crtd.
     void sslCrtdHandleReply(const Helper::Reply &reply);
 
     void switchToHttps(HttpRequest *request, Ssl::BumpMode bumpServerMode);
     void parseTlsHandshake();
     bool switchedToHttps() const { return switchedToHttps_; }
     Ssl::ServerBump *serverBump() {return sslServerBump;}
     inline void setServerBump(Ssl::ServerBump *srvBump) {
         if (!sslServerBump)
             sslServerBump = srvBump;
         else
             assert(sslServerBump == srvBump);
     }
     const SBuf &sslCommonName() const {return sslCommonName_;}
     void resetSslCommonName(const char *name) {sslCommonName_ = name;}
+    const SBuf &tlsClientSni() const { return tlsClientSni_; }
     /// Fill the certAdaptParams with the required data for certificate adaptation
     /// and create the key for storing/retrieve the certificate to/from the cache
     void buildSslCertGenerationParams(Ssl::CertificateProperties &certProperties);
     /// Called when the client sends the first request on a bumped connection.
     /// Returns false if no [delayed] error should be written to the client.
     /// Otherwise, writes the error to the client and returns true. Also checks
     /// for SQUID_X509_V_ERR_DOMAIN_MISMATCH on bumped requests.
     bool serveDelayedError(Http::Stream *);
 
     Ssl::BumpMode sslBumpMode; ///< ssl_bump decision (Ssl::bumpEnd if n/a).
 
     /// Tls parser to use for client HELLO messages parsing on bumped
     /// connections.
     Security::HandshakeParser tlsParser;
 #else
     bool switchedToHttps() const { return false; }
 #endif
 
     /// handle a control message received by context from a peer and call back
     virtual bool writeControlMsgAndCall(HttpReply *rep, AsyncCall::Pointer &call) = 0;
@@ -356,40 +357,41 @@
     bool proxyProtocolError(const char *reason);
 
     /// whether PROXY protocol header is still expected
     bool needProxyProtocolHeader_;
 
 #if USE_AUTH
     /// some user details that can be used to perform authentication on this connection
     Auth::UserRequest::Pointer auth_;
 #endif
 
     /// the parser state for current HTTP/1.x input buffer processing
     Http1::RequestParserPointer parser_;
 
 #if USE_OPENSSL
     bool switchedToHttps_;
     bool parsingTlsHandshake; ///< whether we are getting/parsing TLS Hello bytes
 
     /// The SSL server host name appears in CONNECT request or the server ip address for the intercepted requests
     String sslConnectHostOrIp; ///< The SSL server host name as passed in the CONNECT request
     SBuf sslCommonName_; ///< CN name for SSL certificate generation
+    SBuf tlsClientSni_; ///< the TLS client SNI name
     String sslBumpCertKey; ///< Key to use to store/retrieve generated certificate
 
     /// HTTPS server cert. fetching state for bump-ssl-server-first
     Ssl::ServerBump *sslServerBump;
     Ssl::CertSignAlgorithm signAlgorithm; ///< The signing algorithm to use
 #endif
 
     /// the reason why we no longer write the response or nil
     const char *stoppedSending_;
     /// the reason why we no longer read the request or nil
     const char *stoppedReceiving_;
     /// Connection annotations, clt_conn_tag and other tags are stored here.
     /// If set, are propagated to the current and all future master transactions
     /// on the connection.
     NotePairs::Pointer theNotes;
 };
 
 void setLogUri(ClientHttpRequest * http, char const *uri, bool cleanUrl = false);
 
 const char *findTrailingHTTPVersion(const char *uriAndHTTPVersion, const char *end = NULL);

=== modified file 'src/format/Format.cc'
--- src/format/Format.cc	2017-03-03 12:12:01 +0000
+++ src/format/Format.cc	2017-05-26 09:36:03 +0000
@@ -1219,43 +1219,45 @@
         case LFT_SSL_USER_CERT_SUBJECT:
             if (X509 *cert = al->cache.sslClientCert.get()) {
                 if (X509_NAME *subject = X509_get_subject_name(cert)) {
                     X509_NAME_oneline(subject, tmp, sizeof(tmp));
                     out = tmp;
                 }
             }
             break;
 
         case LFT_SSL_USER_CERT_ISSUER:
             if (X509 *cert = al->cache.sslClientCert.get()) {
                 if (X509_NAME *issuer = X509_get_issuer_name(cert)) {
                     X509_NAME_oneline(issuer, tmp, sizeof(tmp));
                     out = tmp;
                 }
             }
             break;
 
         case LFT_SSL_CLIENT_SNI:
             if (al->request && al->request->clientConnectionManager.valid()) {
-                if (Ssl::ServerBump * srvBump = al->request->clientConnectionManager->serverBump()) {
-                    if (!srvBump->clientSni.isEmpty())
-                        out = srvBump->clientSni.c_str();
+                if (const ConnStateData *conn = al->request->clientConnectionManager.get()) {
+                    if (!conn->tlsClientSni().isEmpty()) {
+                        sb = conn->tlsClientSni();
+                        out = sb.c_str();
+                    }
                 }
             }
             break;
 
         case LFT_SSL_SERVER_CERT_ERRORS:
             if (al->request && al->request->clientConnectionManager.valid()) {
                 if (Ssl::ServerBump * srvBump = al->request->clientConnectionManager->serverBump()) {
                     const char *separator = fmt->data.string ? fmt->data.string : ":";
                     for (const Security::CertErrors *sslError = srvBump->sslErrors(); sslError; sslError = sslError->next) {
                         if (!sb.isEmpty())
                             sb.append(separator);
                         if (const char *errorName = Ssl::GetErrorName(sslError->element.code))
                             sb.append(errorName);
                         else
                             sb.append(sslErrorName(sslError->element.code, tmp, sizeof(tmp)));
                         if (sslError->element.depth >= 0)
                             sb.appendf("@depth=%d", sslError->element.depth);
                     }
                     if (!sb.isEmpty())
                         out = sb.c_str();

=== modified file 'src/ssl/ServerBump.h'
--- src/ssl/ServerBump.h	2017-01-12 13:26:45 +0000
+++ src/ssl/ServerBump.h	2017-05-25 08:42:05 +0000
@@ -30,31 +30,30 @@
     CBDATA_CLASS(ServerBump);
 
 public:
     explicit ServerBump(HttpRequest *fakeRequest, StoreEntry *e = NULL, Ssl::BumpMode mode = Ssl::bumpServerFirst);
     ~ServerBump();
     void attachServerSession(const Security::SessionPointer &); ///< Sets the server TLS session object
     const Security::CertErrors *sslErrors() const; ///< SSL [certificate validation] errors
 
     /// faked, minimal request; required by Client API
     HttpRequest::Pointer request;
     StoreEntry *entry; ///< for receiving Squid-generated error messages
     /// HTTPS server certificate. Maybe it is different than the one
     /// it is stored in serverSession object (error SQUID_X509_V_ERR_CERT_CHANGE)
     Security::CertPointer serverCert;
     struct {
         Ssl::BumpMode step1; ///< The SSL bump mode at step1
         Ssl::BumpMode step2; ///< The SSL bump mode at step2
         Ssl::BumpMode step3; ///< The SSL bump mode at step3
     } act; ///< bumping actions at various bumping steps
     Ssl::BumpStep step; ///< The SSL bumping step
-    SBuf clientSni; ///< the SSL client SNI name
 
 private:
     Security::SessionPointer serverSession; ///< The TLS session object on server side.
     store_client *sc; ///< dummy client to prevent entry trimming
 };
 
 } // namespace Ssl
 
 #endif
 

_______________________________________________
squid-dev mailing list
squid-dev@lists.squid-cache.org
http://lists.squid-cache.org/listinfo/squid-dev

Reply via email to