Hi all,

This patch adds server_name ACL matching server name(s) obtained from various sources such as CONNECT request URI, client SNI, and SSL server certificate CN.

During each SslBump step, Squid improves its understanding of a "true server name", with a bias towards server-provided (and Squid-validated) information.

The server-provided server names are retrieved from the server certificate CN and Subject Alternate Names. The new server_name ACL matches any of alternate names and CN. If the CN or an alternate name is a wildcard, then the new ACL matches any domain that matches the domain with the wildcard.

Other than supporting many sources of server name information (including sources that may supply Squid with multiple server name variants and wildcards), the new ACL is similar to dstdomain.

Also added a server_name_regex ACL.
Add server_name ACL matching server name(s) obtained from various sources
such as CONNECT request URI, client SNI, and SSL server certificate CN.

During each SslBump step, Squid improves its understanding of a "true server
name", with a bias towards server-provided (and Squid-validated) information.

The server-provided server names are retrieved from the server certificate CN
and Subject Alternate Names. The new server_name ACL matches any of alternate
names and CN. If the CN or an alternate name is a wildcard, then the new ACL
matches any domain that matches the domain with the wildcard.

Other than supporting many sources of server name information (including
sources that may supply Squid with multiple server name variants and
wildcards), the new ACL is similar to dstdomain.

Also added a server_name_regex ACL.


This is a Measurement Factory project.
=== modified file 'src/AclRegs.cc'
--- src/AclRegs.cc	2015-01-16 18:12:04 +0000
+++ src/AclRegs.cc	2015-02-13 11:39:50 +0000
@@ -57,40 +57,41 @@
 #include "acl/Note.h"
 #include "acl/NoteData.h"
 #include "acl/PeerName.h"
 #include "acl/Protocol.h"
 #include "acl/ProtocolData.h"
 #include "acl/Random.h"
 #include "acl/Referer.h"
 #include "acl/RegexData.h"
 #include "acl/ReplyHeaderStrategy.h"
 #include "acl/ReplyMimeType.h"
 #include "acl/RequestHeaderStrategy.h"
 #include "acl/RequestMimeType.h"
 #include "acl/SourceAsn.h"
 #include "acl/SourceDomain.h"
 #include "acl/SourceIp.h"
 #include "acl/SquidError.h"
 #include "acl/SquidErrorData.h"
 #if USE_OPENSSL
 #include "acl/Certificate.h"
 #include "acl/CertificateData.h"
+#include "acl/ServerName.h"
 #include "acl/SslError.h"
 #include "acl/SslErrorData.h"
 #endif
 #include "acl/Strategised.h"
 #include "acl/Strategy.h"
 #include "acl/StringData.h"
 #if USE_OPENSSL
 #include "acl/ServerCertificate.h"
 #endif
 #include "acl/Tag.h"
 #include "acl/Time.h"
 #include "acl/TimeData.h"
 #include "acl/Url.h"
 #include "acl/UrlLogin.h"
 #include "acl/UrlPath.h"
 #include "acl/UrlPort.h"
 #include "acl/UserData.h"
 #if USE_AUTH
 #include "auth/AclMaxUserIp.h"
 #include "auth/AclProxyAuth.h"
@@ -160,40 +161,46 @@
 ACL::Prototype ACLUrlLogin::RegistryProtoype(&ACLUrlLogin::RegistryEntry_, "urllogin");
 ACLStrategised<char const *> ACLUrlLogin::RegistryEntry_(new ACLRegexData, ACLUrlLoginStrategy::Instance(), "urllogin");
 ACL::Prototype ACLUrlPath::LegacyRegistryProtoype(&ACLUrlPath::RegistryEntry_, "pattern");
 ACL::Prototype ACLUrlPath::RegistryProtoype(&ACLUrlPath::RegistryEntry_, "urlpath_regex");
 ACLStrategised<char const *> ACLUrlPath::RegistryEntry_(new ACLRegexData, ACLUrlPathStrategy::Instance(), "urlpath_regex");
 ACL::Prototype ACLUrlPort::RegistryProtoype(&ACLUrlPort::RegistryEntry_, "port");
 ACLStrategised<int> ACLUrlPort::RegistryEntry_(new ACLIntRange, ACLUrlPortStrategy::Instance(), "port");
 
 #if USE_OPENSSL
 ACL::Prototype ACLSslError::RegistryProtoype(&ACLSslError::RegistryEntry_, "ssl_error");
 ACLStrategised<const Ssl::CertErrors *> ACLSslError::RegistryEntry_(new ACLSslErrorData, ACLSslErrorStrategy::Instance(), "ssl_error");
 ACL::Prototype ACLCertificate::UserRegistryProtoype(&ACLCertificate::UserRegistryEntry_, "user_cert");
 ACLStrategised<X509 *> ACLCertificate::UserRegistryEntry_(new ACLCertificateData (Ssl::GetX509UserAttribute, "*"), ACLCertificateStrategy::Instance(), "user_cert");
 ACL::Prototype ACLCertificate::CARegistryProtoype(&ACLCertificate::CARegistryEntry_, "ca_cert");
 ACLStrategised<X509 *> ACLCertificate::CARegistryEntry_(new ACLCertificateData (Ssl::GetX509CAAttribute, "*"), ACLCertificateStrategy::Instance(), "ca_cert");
 ACL::Prototype ACLServerCertificate::X509FingerprintRegistryProtoype(&ACLServerCertificate::X509FingerprintRegistryEntry_, "server_cert_fingerprint");
 ACLStrategised<X509 *> ACLServerCertificate::X509FingerprintRegistryEntry_(new ACLCertificateData(Ssl::GetX509Fingerprint, "-sha1", true), ACLServerCertificateStrategy::Instance(), "server_cert_fingerprint");
 
 ACL::Prototype ACLAtStep::RegistryProtoype(&ACLAtStep::RegistryEntry_, "at_step");
 ACLStrategised<Ssl::BumpStep> ACLAtStep::RegistryEntry_(new ACLAtStepData, ACLAtStepStrategy::Instance(), "at_step");
+
+ACL::Prototype ACLServerName::LiteralRegistryProtoype(&ACLServerName::LiteralRegistryEntry_, "server_name");
+ACLStrategised<char const *> ACLServerName::LiteralRegistryEntry_(new ACLServerNameData, ACLServerNameStrategy::Instance(), "server_name");
+ACL::Prototype ACLServerName::RegexRegistryProtoype(&ACLServerName::RegexRegistryEntry_, "server_name_regex");
+ACLFlag  ServerNameRegexFlags[] = {ACL_F_REGEX_CASE, ACL_F_END};
+ACLStrategised<char const *> ACLServerName::RegexRegistryEntry_(new ACLRegexData, ACLServerNameStrategy::Instance(), "server_name_regex", ServerNameRegexFlags);
 #endif
 
 #if USE_SQUID_EUI
 ACL::Prototype ACLARP::RegistryProtoype(&ACLARP::RegistryEntry_, "arp");
 ACLARP ACLARP::RegistryEntry_("arp");
 ACL::Prototype ACLEui64::RegistryProtoype(&ACLEui64::RegistryEntry_, "eui64");
 ACLEui64 ACLEui64::RegistryEntry_("eui64");
 #endif
 
 #if USE_IDENT
 ACL::Prototype ACLIdent::UserRegistryProtoype(&ACLIdent::UserRegistryEntry_, "ident");
 ACLIdent ACLIdent::UserRegistryEntry_(new ACLUserData, "ident");
 ACL::Prototype ACLIdent::RegexRegistryProtoype(&ACLIdent::RegexRegistryEntry_, "ident_regex" );
 ACLIdent ACLIdent::RegexRegistryEntry_(new ACLRegexData, "ident_regex");
 #endif
 
 #if USE_AUTH
 ACL::Prototype ACLProxyAuth::UserRegistryProtoype(&ACLProxyAuth::UserRegistryEntry_, "proxy_auth");
 ACLProxyAuth ACLProxyAuth::UserRegistryEntry_(new ACLUserData, "proxy_auth");
 ACL::Prototype ACLProxyAuth::RegexRegistryProtoype(&ACLProxyAuth::RegexRegistryEntry_, "proxy_auth_regex" );

=== modified file 'src/URL.h'
--- src/URL.h	2015-01-13 07:25:36 +0000
+++ src/URL.h	2015-02-24 20:22:48 +0000
@@ -60,28 +60,59 @@
      * and immutable, only settable at construction time,
      */
     AnyP::UriScheme scheme_;
 
     SBuf userInfo_; // aka 'URL-login'
 };
 
 class HttpRequest;
 class HttpRequestMethod;
 
 AnyP::ProtocolType urlParseProtocol(const char *, const char *e = NULL);
 void urlInitialize(void);
 HttpRequest *urlParse(const HttpRequestMethod&, char *, HttpRequest *request = NULL);
 const char *urlCanonical(HttpRequest *);
 char *urlCanonicalClean(const HttpRequest *);
 const char *urlCanonicalFakeHttps(const HttpRequest * request);
 bool urlIsRelative(const char *);
 char *urlMakeAbsolute(const HttpRequest *, const char *);
 char *urlRInternal(const char *host, unsigned short port, const char *dir, const char *name);
 char *urlInternal(const char *dir, const char *name);
-int matchDomainName(const char *host, const char *domain);
+
+/**
+ * matchDomainName() compares a hostname (usually extracted from traffic)
+ * with a domainname (usually from an ACL) according to the following rules:
+ *
+ *    HOST      |   DOMAIN    |   MATCH?
+ * -------------|-------------|------
+ *    foo.com   |   foo.com   |     YES
+ *   .foo.com   |   foo.com   |     YES
+ *  x.foo.com   |   foo.com   |     NO
+ *    foo.com   |  .foo.com   |     YES
+ *   .foo.com   |  .foo.com   |     YES
+ *  x.foo.com   |  .foo.com   |     YES
+ *
+ *  We strip leading dots on hosts (but not domains!) so that
+ *  ".foo.com" is always the same as "foo.com".
+ *
+ * if honorWildcards is true then the matchDomainName() also accepts
+ * optional wildcards on hostname:
+ *
+ *    HOST      |    DOMAIN    |  MATCH?
+ * -------------|--------------|-------
+ *    *.foo.com |   x.foo.com  |   YES
+ *    *.foo.com |  .x.foo.com  |   YES
+ *    *.foo.com |    .foo.com  |   YES
+ *    *.foo.com |     foo.com  |   NO
+ *
+ * \retval 0 means the host matches the domain
+ * \retval 1 means the host is greater than the domain
+ * \retval -1 means the host is less than the domain
+ */
+int matchDomainName(const char *host, const char *domain, bool honorWildcards = false);
 int urlCheckRequest(const HttpRequest *);
 int urlDefaultPort(AnyP::ProtocolType p);
 char *urlHostname(const char *url);
 void urlExtMethodConfigure(void);
 
 #endif /* SQUID_SRC_URL_H_H */
 

=== modified file 'src/acl/DomainData.h'
--- src/acl/DomainData.h	2015-01-13 07:25:36 +0000
+++ src/acl/DomainData.h	2015-02-13 11:15:29 +0000
@@ -2,31 +2,31 @@
  * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
  *
  * Squid software is distributed under GPLv2+ license and includes
  * contributions from numerous individuals and organizations.
  * Please see the COPYING and CONTRIBUTORS files for details.
  */
 
 #ifndef SQUID_ACLDOMAINDATA_H
 #define SQUID_ACLDOMAINDATA_H
 
 #include "acl/Acl.h"
 #include "acl/Data.h"
 #include "splay.h"
 
 class ACLDomainData : public ACLData<char const *>
 {
     MEMPROXY_CLASS(ACLDomainData);
 
 public:
     virtual ~ACLDomainData();
-    bool match(char const *);
+    virtual bool match(char const *);
     virtual SBufList dump() const;
     void parse();
     bool empty() const;
     virtual ACLData<char const *> *clone() const;
 
     Splay<char *> *domains;
 };
 
 #endif /* SQUID_ACLDOMAINDATA_H */
 

=== modified file 'src/acl/Makefile.am'
--- src/acl/Makefile.am	2015-02-02 20:02:55 +0000
+++ src/acl/Makefile.am	2015-02-09 16:37:08 +0000
@@ -94,40 +94,42 @@
 	Note.h \
 	Note.cc \
 	NoteData.h \
 	NoteData.cc \
 	PeerName.cc \
 	PeerName.h \
 	Protocol.cc \
 	ProtocolData.cc \
 	ProtocolData.h \
 	Protocol.h \
 	Random.cc \
 	Random.h \
 	Referer.cc \
 	Referer.h \
 	ReplyHeaderStrategy.h \
 	ReplyMimeType.cc \
 	ReplyMimeType.h \
 	RequestHeaderStrategy.h \
 	RequestMimeType.cc \
 	RequestMimeType.h \
+	ServerName.cc \
+	ServerName.h \
 	SourceAsn.h \
 	SourceDomain.cc \
 	SourceDomain.h \
 	SourceIp.cc \
 	SourceIp.h \
 	SquidError.h \
 	SquidError.cc \
 	SquidErrorData.cc \
 	SquidErrorData.h \
 	Tag.cc \
 	Tag.h \
 	Url.cc \
 	Url.h \
 	UrlLogin.cc \
 	UrlLogin.h \
 	UrlPath.cc \
 	UrlPath.h \
 	UrlPort.cc \
 	UrlPort.h \
 	UserData.cc \

=== added file 'src/acl/ServerName.cc'
--- src/acl/ServerName.cc	1970-01-01 00:00:00 +0000
+++ src/acl/ServerName.cc	2015-02-23 20:38:25 +0000
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
+ *
+ * Squid software is distributed under GPLv2+ license and includes
+ * contributions from numerous individuals and organizations.
+ * Please see the COPYING and CONTRIBUTORS files for details.
+ */
+
+/* DEBUG: section 28    Access Control */
+
+#include "squid.h"
+#include "acl/Checklist.h"
+#include "acl/ServerName.h"
+#include "acl/DomainData.h"
+#include "acl/RegexData.h"
+#include "client_side.h"
+#include "fde.h"
+#include "HttpRequest.h"
+#include "ipcache.h"
+#include "SquidString.h"
+#include "ssl/bio.h"
+#include "ssl/ServerBump.h"
+#include "ssl/support.h"
+#include "URL.h"
+
+// Compare function for tree search algorithms
+static int
+aclHostDomainCompare( char *const &a, char * const &b)
+{
+    const char *h = static_cast<const char *>(a);
+    const char *d = static_cast<const char *>(b);
+    debugs(28, 7, "Match:" << h << " <>  " << d);
+    return matchDomainName(h, d, true);
+}
+
+bool
+ACLServerNameData::match(const char *host)
+{
+    if (host == NULL)
+        return 0;
+
+    debugs(28, 3, "checking '" << host << "'");
+
+    char *h = const_cast<char *>(host);
+    char const * const * result = domains->find(h, aclHostDomainCompare);
+
+    debugs(28, 3, "'" << host << "' " << (result ? "found" : "NOT found"));
+
+    return (result != NULL);
+
+}
+
+ACLData<char const *> *
+ACLServerNameData::clone() const
+{
+    /* Splay trees don't clone yet. */
+    assert (!domains);
+    return new ACLServerNameData;
+}
+
+/// A helper function to be used with Ssl::matchX509CommonNames().
+/// \retval 0 when the name (cn or an alternate name) matches acl data
+/// \retval 1 when the name does not match
+template<class MatchType>
+int
+check_cert_domain( void *check_data, ASN1_STRING *cn_data)
+{
+    char cn[1024];
+    ACLData<MatchType> * data = (ACLData<MatchType> *)check_data;
+
+    if (cn_data->length > (int)sizeof(cn) - 1)
+        return 1; // ignore data that does not fit our buffer
+ 
+    memcpy(cn, cn_data->data, cn_data->length);
+    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, ACLFlags &flags)
+{
+    assert(checklist != NULL && checklist->request != NULL);
+
+    if (checklist->conn() && checklist->conn()->serverBump()) {
+        if (X509 *peer_cert = checklist->conn()->serverBump()->serverCert.get()) {
+            if (Ssl::matchX509CommonNames(peer_cert, (void *)data, check_cert_domain<MatchType>))
+                return 1;
+        }
+    }
+
+    const char *serverName = NULL;
+    if (checklist->conn() && !checklist->conn()->sslCommonName().isEmpty()) {
+        SBuf scn = checklist->conn()->sslCommonName();
+        serverName = scn.c_str();
+    }
+
+    if (serverName == NULL)
+        serverName = checklist->request->GetHost();
+
+    if (serverName && data->match(serverName)) {
+        return 1;
+    }
+
+    return data->match("none");
+}
+
+ACLServerNameStrategy *
+ACLServerNameStrategy::Instance()
+{
+    return &Instance_;
+}
+
+ACLServerNameStrategy ACLServerNameStrategy::Instance_;
+

=== added file 'src/acl/ServerName.h'
--- src/acl/ServerName.h	1970-01-01 00:00:00 +0000
+++ src/acl/ServerName.h	2015-02-13 19:36:32 +0000
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
+ *
+ * Squid software is distributed under GPLv2+ license and includes
+ * contributions from numerous individuals and organizations.
+ * Please see the COPYING and CONTRIBUTORS files for details.
+ */
+
+#ifndef SQUID_ACLSERVERNAME_H
+#define SQUID_ACLSERVERNAME_H
+
+#include "acl/Acl.h"
+#include "acl/Checklist.h"
+#include "acl/Data.h"
+#include "acl/Strategised.h"
+#include "acl/DomainData.h"
+
+class ACLServerNameData : public ACLDomainData {
+    MEMPROXY_CLASS(ACLServerNameData);
+public:
+    ACLServerNameData() : ACLDomainData(){}
+    virtual bool match(const char *);
+    virtual ACLData<char const *> *clone() const;
+};
+
+/// \ingroup ACLAPI
+class ACLServerNameStrategy : public ACLStrategy<char const *>
+{
+
+public:
+    virtual int match (ACLData<MatchType> * &, ACLFilledChecklist *, ACLFlags &);
+    static ACLServerNameStrategy *Instance();
+    virtual bool requiresRequest() const {return true;}
+
+    /**
+     * Not implemented to prevent copies of the instance.
+     \par
+     * Not private to prevent brain dead g+++ warnings about
+     * private constructors with no friends
+     */
+    ACLServerNameStrategy(ACLServerNameStrategy const &);
+
+private:
+    static ACLServerNameStrategy Instance_;
+    ACLServerNameStrategy() {}
+
+    ACLServerNameStrategy&operator=(ACLServerNameStrategy const &);
+};
+
+/// \ingroup ACLAPI
+class ACLServerName
+{
+
+private:
+    static ACL::Prototype LiteralRegistryProtoype;
+    static ACLStrategised<char const *> LiteralRegistryEntry_;
+    static ACL::Prototype RegexRegistryProtoype;
+    static ACLStrategised<char const *> RegexRegistryEntry_;
+};
+
+#endif /* SQUID_ACLSERVERNAME_H */
+

=== modified file 'src/cf.data.pre'
--- src/cf.data.pre	2015-01-31 16:34:08 +0000
+++ src/cf.data.pre	2015-02-24 20:23:51 +0000
@@ -1096,40 +1096,52 @@
 
 	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 server_name .foo.com ...
+	  # matches server name obtained from various sources [fast]
+	  #
+	  # The server name is obtained during SslBmp steps from such sources
+	  # as CONNECT request URI, client SNI, and SSL server certificate CN.
+	  # During each SslBump step, Squid may improve its understanding of a
+	  # "true server name". Unlike dstdomain, this ACL does not perform
+	  # DNS lookups.
+
+	acl aclname server_name_regex [-i] \.foo\.com ...
+	  # regex matches server name obtained from various sources [fast]
 ENDIF
 	acl aclname any-of acl1 acl2 ...
 	  # match any one of the acls [fast or slow]
 	  # The first matching ACL stops further ACL evaluation.
 	  #
 	  # ACLs from multiple any-of lines with the same name are ORed.
 	  # For example, A = (a1 or a2) or (a3 or a4) can be written as
 	  #   acl A any-of a1 a2
 	  #   acl A any-of a3 a4
 	  #
 	  # This group ACL is fast if all evaluated ACLs in the group are fast
 	  # and slow otherwise.
 
 	acl aclname all-of acl1 acl2 ... 
 	  # match all of the acls [fast or slow]
 	  # The first mismatching ACL stops further ACL evaluation.
 	  #
 	  # ACLs from multiple all-of lines with the same name are ORed.
 	  # For example, B = (b1 and b2) or (b3 and b4) can be written as
 	  #   acl B all-of b1 b2

=== modified file 'src/client_side.cc'
--- src/client_side.cc	2015-02-06 19:45:02 +0000
+++ src/client_side.cc	2015-02-23 19:49:38 +0000
@@ -3679,40 +3679,48 @@
     debugs(83, 3, "clientNegotiateSSL: FD " << fd << " negotiated cipher " <<
            SSL_get_cipher(ssl));
 
     client_cert = SSL_get_peer_certificate(ssl);
 
     if (client_cert != NULL) {
         debugs(83, 3, "clientNegotiateSSL: FD " << fd <<
                " client certificate: subject: " <<
                X509_NAME_oneline(X509_get_subject_name(client_cert), 0, 0));
 
         debugs(83, 3, "clientNegotiateSSL: FD " << fd <<
                " client certificate: issuer: " <<
                X509_NAME_oneline(X509_get_issuer_name(client_cert), 0, 0));
 
         X509_free(client_cert);
     } else {
         debugs(83, 5, "clientNegotiateSSL: FD " << fd <<
                " has no certificate.");
     }
 
+#if defined(TLSEXT_NAMETYPE_host_name)
+    if (!conn->serverBump()) {
+        // when in bumpClientFirst mode, get the server name from SNI
+        if (const char *server = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name))
+            conn->resetSslCommonName(server);
+    }
+#endif
+
     conn->readSomeData();
 }
 
 /**
  * If SSL_CTX is given, starts reading the SSL handshake.
  * Otherwise, calls switchToHttps to generate a dynamic SSL_CTX.
  */
 static void
 httpsEstablish(ConnStateData *connState,  SSL_CTX *sslContext, Ssl::BumpMode bumpMode)
 {
     SSL *ssl = NULL;
     assert(connState);
     const Comm::ConnectionPointer &details = connState->clientConnection;
 
     if (sslContext && !(ssl = httpsCreate(details, sslContext)))
         return;
 
     typedef CommCbMemFunT<ConnStateData, CommTimeoutCbParams> TimeoutDialer;
     AsyncCall::Pointer timeoutCall = JobCallback(33, 5, TimeoutDialer,
                                      connState, ConnStateData::requestTimeout);
@@ -3874,41 +3882,41 @@
                 debugs(33, 5, HERE << "Certificate for " << sslConnectHostOrIp << " was successfully recieved from ssl_crtd");
                 if (sslServerBump && (sslServerBump->act.step1 == Ssl::bumpPeek || sslServerBump->act.step1 == Ssl::bumpStare)) {
                     doPeekAndSpliceStep();
                     SSL *ssl = fd_table[clientConnection->fd].ssl;
                     bool ret = Ssl::configureSSLUsingPkeyAndCertFromMemory(ssl, reply_message.getBody().c_str(), *port);
                     if (!ret)
                         debugs(33, 5, "Failed to set certificates to ssl object for PeekAndSplice mode");
                 } else {
                     SSL_CTX *ctx = Ssl::generateSslContextUsingPkeyAndCertFromMemory(reply_message.getBody().c_str(), *port);
                     getSslContextDone(ctx, true);
                 }
                 return;
             }
         }
     }
     getSslContextDone(NULL);
 }
 
 void ConnStateData::buildSslCertGenerationParams(Ssl::CertificateProperties &certProperties)
 {
-    certProperties.commonName =  sslCommonName.size() > 0 ? sslCommonName.termedBuf() : sslConnectHostOrIp.termedBuf();
+    certProperties.commonName =  sslCommonName_.isEmpty() ? sslConnectHostOrIp.termedBuf() : sslCommonName_.c_str();
 
     // fake certificate adaptation requires bump-server-first mode
     if (!sslServerBump) {
         assert(port->signingCert.get());
         certProperties.signWithX509.resetAndLock(port->signingCert.get());
         if (port->signPkey.get())
             certProperties.signWithPkey.resetAndLock(port->signPkey.get());
         certProperties.signAlgorithm = Ssl::algSignTrusted;
         return;
     }
 
     // In case of an error while connecting to the secure server, use a fake
     // trusted certificate, with no mimicked fields and no adaptation
     // algorithms. There is nothing we can mimic so we want to minimize the
     // number of warnings the user will have to see to get to the error page.
     assert(sslServerBump->entry);
     if (sslServerBump->entry->isEmpty()) {
         if (X509 *mimicCert = sslServerBump->serverCert.get())
             certProperties.mimicCert.resetAndLock(mimicCert);
 
@@ -4095,41 +4103,41 @@
     // bumped intercepted conns should already have Config.Timeout.request set
     // but forwarded connections may only have Config.Timeout.lifetime. [Re]set
     // to make sure the connection does not get stuck on non-SSL clients.
     typedef CommCbMemFunT<ConnStateData, CommTimeoutCbParams> TimeoutDialer;
     AsyncCall::Pointer timeoutCall = JobCallback(33, 5, TimeoutDialer,
                                      this, ConnStateData::requestTimeout);
     commSetConnTimeout(clientConnection, Config.Timeout.request, timeoutCall);
 
     // Disable the client read handler until CachePeer selection is complete
     Comm::SetSelect(clientConnection->fd, COMM_SELECT_READ, NULL, NULL, 0);
     Comm::SetSelect(clientConnection->fd, COMM_SELECT_READ, clientNegotiateSSL, this, 0);
     switchedToHttps_ = true;
 }
 
 void
 ConnStateData::switchToHttps(HttpRequest *request, Ssl::BumpMode bumpServerMode)
 {
     assert(!switchedToHttps_);
 
     sslConnectHostOrIp = request->GetHost();
-    sslCommonName = request->GetHost();
+    resetSslCommonName(request->GetHost());
 
     // We are going to read new request
     flags.readMore = true;
     debugs(33, 5, HERE << "converting " << clientConnection << " to SSL");
 
     // keep version major.minor details the same.
     // but we are now performing the HTTPS handshake traffic
     transferProtocol.protocol = AnyP::PROTO_HTTPS;
 
     // If sslServerBump is set, then we have decided to deny CONNECT
     // and now want to switch to SSL to send the error to the client
     // without even peeking at the origin server certificate.
     if (bumpServerMode == Ssl::bumpServerFirst && !sslServerBump) {
         request->flags.sslPeek = true;
         sslServerBump = new Ssl::ServerBump(request);
 
         // will call httpsPeeked() with certificate and connection, eventually
         FwdState::fwdStart(clientConnection, sslServerBump->entry, sslServerBump->request.getRaw());
         return;
     } else if (bumpServerMode == Ssl::bumpPeek || bumpServerMode == Ssl::bumpStare) {
@@ -4172,42 +4180,44 @@
     int ret = 0;
     if ((ret = Squid_SSL_accept(conn, clientPeekAndSpliceSSL)) < 0)
         debugs(83, 2, "SSL_accept failed.");
 
     BIO *b = SSL_get_rbio(ssl);
     assert(b);
     Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(b->ptr);
     if (ret < 0) {
         const err_type err = bio->noSslClient() ? ERR_PROTOCOL_UNKNOWN : ERR_SECURE_ACCEPT_FAIL;
         if (!conn->spliceOnError(err))
             conn->clientConnection->close();
         return;
     }
 
     if (bio->rBufData().contentSize() > 0)
         conn->receivedFirstByte();
 
     if (bio->gotHello()) {
         if (conn->serverBump()) {
             Ssl::Bio::sslFeatures const &features = bio->getFeatures();
-            if (!features.serverName.isEmpty())
+            if (!features.serverName.isEmpty()) {
                 conn->serverBump()->clientSni = features.serverName;
+                conn->resetSslCommonName(features.serverName.c_str());
+            }
         }
 
         debugs(83, 5, "I got hello. Start forwarding the request!!! ");
         Comm::SetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
         Comm::SetSelect(fd, COMM_SELECT_WRITE, NULL, NULL, 0);
         conn->startPeekAndSpliceDone();
         return;
     }
 }
 
 void ConnStateData::startPeekAndSplice()
 {
     // will call httpsPeeked() with certificate and connection, eventually
     SSL_CTX *unConfiguredCTX = Ssl::createSSLContext(port->signingCert, port->signPkey, *port);
     fd_table[clientConnection->fd].dynamicSslContext = unConfiguredCTX;
 
     if (!httpsCreate(clientConnection, unConfiguredCTX))
         return;
 
     // commSetConnTimeout() was called for this request before we switched.
@@ -4328,64 +4338,45 @@
 ConnStateData::doPeekAndSpliceStep()
 {
     SSL *ssl = fd_table[clientConnection->fd].ssl;
     BIO *b = SSL_get_rbio(ssl);
     assert(b);
     Ssl::ClientBio *bio = static_cast<Ssl::ClientBio *>(b->ptr);
 
     debugs(33, 5, "PeekAndSplice mode, proceed with client negotiation. Currrent state:" << SSL_state_string_long(ssl));
     bio->hold(false);
 
     Comm::SetSelect(clientConnection->fd, COMM_SELECT_WRITE, clientNegotiateSSL, this, 0);
     switchedToHttps_ = true;
 }
 
 void
 ConnStateData::httpsPeeked(Comm::ConnectionPointer serverConnection)
 {
     Must(sslServerBump != NULL);
 
     if (Comm::IsConnOpen(serverConnection)) {
-        SSL *ssl = fd_table[serverConnection->fd].ssl;
-        assert(ssl);
-        Ssl::X509_Pointer serverCert(SSL_get_peer_certificate(ssl));
-        assert(serverCert.get() != NULL);
-        sslCommonName = Ssl::CommonHostName(serverCert.get());
-        debugs(33, 5, HERE << "HTTPS server CN: " << sslCommonName <<
-               " bumped: " << *serverConnection);
-
         pinConnection(serverConnection, NULL, NULL, false);
 
         debugs(33, 5, HERE << "bumped HTTPS server: " << sslConnectHostOrIp);
     } else {
         debugs(33, 5, HERE << "Error while bumping: " << sslConnectHostOrIp);
-        Ip::Address intendedDest;
-        intendedDest = sslConnectHostOrIp.termedBuf();
-        const bool isConnectRequest = !port->flags.isIntercepted();
-
-        // Squid serves its own error page and closes, so we want
-        // a CN that causes no additional browser errors. Possible
-        // only when bumping CONNECT with a user-typed address.
-        if (intendedDest.isAnyAddr() || isConnectRequest)
-            sslCommonName = sslConnectHostOrIp;
-        else if (sslServerBump->serverCert.get())
-            sslCommonName = Ssl::CommonHostName(sslServerBump->serverCert.get());
 
         //  copy error detail from bump-server-first request to CONNECT request
         if (currentobject != NULL && currentobject->http != NULL && currentobject->http->request)
             currentobject->http->request->detailError(sslServerBump->request->errType, sslServerBump->request->errDetail);
     }
 
     getSslContextStart();
 }
 
 #endif /* USE_OPENSSL */
 
 /// check FD after clientHttp[s]ConnectionOpened, adjust HttpSockets as needed
 static bool
 OpenedHttpSocket(const Comm::ConnectionPointer &c, const Ipc::FdNoteId portType)
 {
     if (!Comm::IsConnOpen(c)) {
         Must(NHttpSockets > 0); // we tried to open some
         --NHttpSockets; // there will be fewer sockets than planned
         Must(HttpSockets[NHttpSockets] < 0); // no extra fds received
 

=== modified file 'src/client_side.h'
--- src/client_side.h	2015-01-16 18:12:04 +0000
+++ src/client_side.h	2015-02-13 16:50:56 +0000
@@ -363,40 +363,42 @@
     /**
      * Done create dynamic ssl certificate.
      *
      * \param[in] isNew if generated certificate is new, so we need to add this certificate to storage.
      */
     void getSslContextDone(SSL_CTX * sslContext, 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);
     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;}
     /// 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(ClientSocketContext *context);
 
     Ssl::BumpMode sslBumpMode; ///< ssl_bump decision (Ssl::bumpEnd if n/a).
 
 #else
     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; }
 
     /// handle a control message received by context from a peer and call back
@@ -454,41 +456,41 @@
     bool parseProxyProtocolHeader();
     bool parseProxy1p0();
     bool parseProxy2p0();
     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_;
     /// 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
-    String sslCommonName; ///< CN name for SSL certificate generation
+    SBuf sslCommonName_; ///< CN name for SSL certificate generation
     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_;
 
     AsyncCall::Pointer reader; ///< set when we are reading
 
     bool receivedFirstByte_; ///< true if at least one byte received on this connection
     SBuf connectionTag_; ///< clt_conn_tag=Tag annotation for client connection
 };
 
 void setLogUri(ClientHttpRequest * http, char const *uri, bool cleanUrl = false);
 

=== modified file 'src/ssl/PeerConnector.cc'
--- src/ssl/PeerConnector.cc	2015-02-09 18:12:05 +0000
+++ src/ssl/PeerConnector.cc	2015-02-23 19:59:32 +0000
@@ -28,41 +28,42 @@
 #include "ssl/helper.h"
 #include "ssl/PeerConnector.h"
 #include "ssl/ServerBump.h"
 #include "ssl/support.h"
 
 CBDATA_NAMESPACED_CLASS_INIT(Ssl, PeerConnector);
 
 Ssl::PeerConnector::PeerConnector(
     HttpRequestPointer &aRequest,
     const Comm::ConnectionPointer &aServerConn,
     const Comm::ConnectionPointer &aClientConn,
     AsyncCall::Pointer &aCallback,
     const time_t timeout):
     AsyncJob("Ssl::PeerConnector"),
     request(aRequest),
     serverConn(aServerConn),
     clientConn(aClientConn),
     callback(aCallback),
     negotiationTimeout(timeout),
     startTime(squid_curtime),
-    splice(false)
+    splice(false),
+    serverCertificateHandled(false)
 {
     // if this throws, the caller's cb dialer is not our CbDialer
     Must(dynamic_cast<CbDialer*>(callback->getDialer()));
 }
 
 Ssl::PeerConnector::~PeerConnector()
 {
     debugs(83, 5, "Peer connector " << this << " gone");
 }
 
 bool Ssl::PeerConnector::doneAll() const
 {
     return (!callback || callback->canceled()) && AsyncJob::doneAll();
 }
 
 /// Preps connection and SSL state. Calls negotiate().
 void
 Ssl::PeerConnector::start()
 {
     AsyncJob::start();
@@ -243,51 +244,76 @@
     SSL *ssl = fd_table[fd].ssl;
     const int result = SSL_connect(ssl);
     if (result <= 0) {
         handleNegotiateError(result);
         return; // we might be gone by now
     }
 
     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 (!sslFinalized())
         return;
 
     callBack();
 }
 
+void
+Ssl::PeerConnector::handleServerCertificate()
+{
+    if (serverCertificateHandled)
+        return;
+
+    if (ConnStateData *csd = request->clientConnectionManager.valid()) {
+        const int fd = serverConnection()->fd;
+        SSL *ssl = fd_table[fd].ssl;
+        Ssl::X509_Pointer serverCert(SSL_get_peer_certificate(ssl));
+        if (!serverCert.get())
+            return;
+
+        serverCertificateHandled = true;
+
+        csd->resetSslCommonName(Ssl::CommonHostName(serverCert.get()));
+        debugs(83, 5, HERE << "HTTPS server CN: " << csd->sslCommonName() <<
+               " bumped: " << *serverConnection());
+
+        // remember the server certificate for later use
+        if (Ssl::ServerBump *serverBump = csd->serverBump()) {
+            serverBump->serverCert.reset(serverCert.release());   
+        }
+    }
+}
+
 bool
 Ssl::PeerConnector::sslFinalized()
 {
     const int fd = serverConnection()->fd;
     SSL *ssl = fd_table[fd].ssl;
 
-    if (request->clientConnectionManager.valid()) {
-        // remember the server certificate from the ErrorDetail object
-        if (Ssl::ServerBump *serverBump = request->clientConnectionManager->serverBump()) {
-            serverBump->serverCert.reset(SSL_get_peer_certificate(ssl));
+    handleServerCertificate();
 
+    if (ConnStateData *csd = request->clientConnectionManager.valid()) {
+        if (Ssl::ServerBump *serverBump = csd->serverBump()) {
             // remember validation errors, if any
             if (Ssl::CertErrors *errs = static_cast<Ssl::CertErrors *>(SSL_get_ex_data(ssl, ssl_ex_index_ssl_errors)))
                 serverBump->sslErrors = cbdataReference(errs);
         }
     }
 
     if (Ssl::TheConfig.ssl_crt_validator) {
         Ssl::CertValidationRequest validationRequest;
         // 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.ssl = ssl;
         validationRequest.domainName = request->GetHost();
         if (Ssl::CertErrors *errs = static_cast<Ssl::CertErrors *>(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 {
@@ -307,50 +333,49 @@
             }
             serverConn->close();
             return true;
         }
     }
     return true;
 }
 
 void switchToTunnel(HttpRequest *request, Comm::ConnectionPointer & clientConn, Comm::ConnectionPointer &srvConn);
 
 void
 Ssl::PeerConnector::cbCheckForPeekAndSpliceDone(allow_t answer, void *data)
 {
     Ssl::PeerConnector *peerConnect = (Ssl::PeerConnector *) data;
     peerConnect->checkForPeekAndSpliceDone((Ssl::BumpMode)answer.kind);
 }
 
 void
 Ssl::PeerConnector::checkForPeekAndSplice()
 {
-    SSL *ssl = fd_table[serverConn->fd].ssl;
     // Mark Step3 of bumping
     if (request->clientConnectionManager.valid()) {
         if (Ssl::ServerBump *serverBump = request->clientConnectionManager->serverBump()) {
             serverBump->step = Ssl::bumpStep3;
-            if (!serverBump->serverCert.get())
-                serverBump->serverCert.reset(SSL_get_peer_certificate(ssl));
         }
     }
 
+    handleServerCertificate();
+
     ACLFilledChecklist *acl_checklist = new ACLFilledChecklist(
         ::Config.accessList.ssl_bump,
         request.getRaw(), NULL);
     acl_checklist->nonBlockingCheck(Ssl::PeerConnector::cbCheckForPeekAndSpliceDone, this);
 }
 
 void
 Ssl::PeerConnector::checkForPeekAndSpliceDone(Ssl::BumpMode const action)
 {
     SSL *ssl = fd_table[serverConn->fd].ssl;
     BIO *b = SSL_get_rbio(ssl);
     Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr);
     debugs(83,5, "Will check for peek and splice on FD " << serverConn->fd);
 
     Ssl::BumpMode finalAction = action;
     // adjust the final bumping mode if needed
     if (finalAction < Ssl::bumpSplice)
         finalAction = Ssl::bumpBump;
 
     if (finalAction == Ssl::bumpSplice && !srvBio->canSplice())

=== modified file 'src/ssl/PeerConnector.h'
--- src/ssl/PeerConnector.h	2015-01-13 07:25:36 +0000
+++ src/ssl/PeerConnector.h	2015-02-23 19:48:06 +0000
@@ -139,45 +139,50 @@
 
 private:
     PeerConnector(const PeerConnector &); // not implemented
     PeerConnector &operator =(const PeerConnector &); // not implemented
 
     /// mimics FwdState to minimize changes to FwdState::initiate/negotiateSsl
     Comm::ConnectionPointer const &serverConnection() const { return serverConn; }
 
     void bail(ErrorState *error); ///< Return an error to the PeerConnector caller
 
     /// Callback the caller class, and pass the ready to communicate secure
     /// connection or an error if PeerConnector failed.
     void callBack();
 
     /// Process response from cert validator helper
     void sslCrtvdHandleReply(Ssl::CertValidationResponse const &);
 
     /// Check SSL errors returned from cert validator against sslproxy_cert_error access list
     Ssl::CertErrors *sslCrtvdCheckForErrors(Ssl::CertValidationResponse const &, Ssl::ErrorDetail *&);
 
+    /// Updates associated client connection manager members
+    /// if the server certificate was received from the server.
+    void handleServerCertificate();
+
     /// Callback function called when squid receive message from cert validator helper
     static void sslCrtvdHandleReplyWrapper(void *data, Ssl::CertValidationResponse const &);
 
     /// A wrapper function for negotiateSsl for use with Comm::SetSelect
     static void NegotiateSsl(int fd, void *data);
 
     /// A wrapper function for checkForPeekAndSpliceDone for use with acl
     static void cbCheckForPeekAndSpliceDone(allow_t answer, void *data);
 
     HttpRequestPointer request; ///< peer connection trigger or cause
     Comm::ConnectionPointer serverConn; ///< TCP connection to the peer
     Comm::ConnectionPointer clientConn; ///< TCP connection to the client
     AsyncCall::Pointer callback; ///< we call this with the results
     AsyncCall::Pointer closeHandler; ///< we call this when the connection closed
     time_t negotiationTimeout; ///< the ssl connection timeout to use
     time_t startTime; ///< when the peer connector negotiation started
     bool splice; ///< Whether we are going to splice or not
+    bool serverCertificateHandled; ///< whether handleServerCertificate() succeeded
 };
 
 std::ostream &operator <<(std::ostream &os, const Ssl::PeerConnectorAnswer &a);
 
 } // namespace Ssl
 
 #endif /* SQUID_PEER_CONNECTOR_H */
 

=== modified file 'src/url.cc'
--- src/url.cc	2015-01-13 07:25:36 +0000
+++ src/url.cc	2015-02-24 12:24:35 +0000
@@ -666,64 +666,42 @@
             urlbuf[urllen] = '/';
             ++urllen;
             strncpy(&urlbuf[urllen], relUrl, MAX_URL - urllen - 1);
         } else {
             ++last_slash;
             size_t pathlen = last_slash - path;
             if (pathlen > MAX_URL - urllen - 1) {
                 pathlen = MAX_URL - urllen - 1;
             }
             strncpy(&urlbuf[urllen], path, pathlen);
             urllen += pathlen;
             if (urllen + 1 < MAX_URL) {
                 strncpy(&urlbuf[urllen], relUrl, MAX_URL - urllen - 1);
             }
         }
     }
 
     return (urlbuf);
 }
 
-/*
- * matchDomainName() compares a hostname with a domainname according
- * to the following rules:
- *
- *    HOST          DOMAIN        MATCH?
- * ------------- -------------    ------
- *    foo.com       foo.com         YES
- *   .foo.com       foo.com         YES
- *  x.foo.com       foo.com          NO
- *    foo.com      .foo.com         YES
- *   .foo.com      .foo.com         YES
- *  x.foo.com      .foo.com         YES
- *
- *  We strip leading dots on hosts (but not domains!) so that
- *  ".foo.com" is is always the same as "foo.com".
- *
- *  Return values:
- *     0 means the host matches the domain
- *     1 means the host is greater than the domain
- *    -1 means the host is less than the domain
- */
-
 int
-matchDomainName(const char *h, const char *d)
+matchDomainName(const char *h, const char *d, bool honorWildcards)
 {
     int dl;
     int hl;
 
     while ('.' == *h)
         ++h;
 
     hl = strlen(h);
 
     dl = strlen(d);
 
     /*
      * Start at the ends of the two strings and work towards the
      * beginning.
      */
     while (xtolower(h[--hl]) == xtolower(d[--dl])) {
         if (hl == 0 && dl == 0) {
             /*
              * We made it all the way to the beginning of both
              * strings without finding any difference.
@@ -746,40 +724,47 @@
                 return -1;
         }
 
         if (0 == dl) {
             /*
              * The domain string is shorter than the host string.
              * This is a match only if the first domain character
              * is a leading '.'.
              */
 
             if ('.' == d[0])
                 return 0;
             else
                 return 1;
         }
     }
 
     /*
      * We found different characters in the same position (from the end).
      */
+
+    // If the h has a form of "*.foo.com" and d has a form of "x.foo.com"
+    // then the h[hl] points to '*', h[hl+1] to '.' and d[dl] to 'x'
+    // The following checks are safe, the "h[hl + 1]" in the worst case is '\0'.
+    if (honorWildcards && h[hl] == '*' && h[hl + 1] == '.')
+        return 0;
+
     /*
      * If one of those character is '.' then its special.  In order
      * for splay tree sorting to work properly, "x-foo.com" must
      * be greater than ".foo.com" even though '-' is less than '.'.
      */
     if ('.' == d[dl])
         return 1;
 
     if ('.' == h[hl])
         return -1;
 
     return (xtolower(h[hl]) - xtolower(d[dl]));
 }
 
 /*
  * return true if we can serve requests for this method.
  */
 int
 urlCheckRequest(const HttpRequest * r)
 {

_______________________________________________
squid-dev mailing list
[email protected]
http://lists.squid-cache.org/listinfo/squid-dev

Reply via email to