| // Copyright (c) 2017 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "quiche/common/platform/api/quiche_hostname_utils.h" | 
 |  | 
 | #include <string> | 
 |  | 
 | #include "absl/strings/string_view.h" | 
 | #include "url/url_canon.h" | 
 | #include "url/url_canon_stdstring.h" | 
 | #include "quiche/common/platform/api/quiche_logging.h" | 
 |  | 
 | namespace quiche { | 
 |  | 
 | // TODO(vasilvv): the functions below are forked from Chromium's | 
 | // net/base/url_util.h; those should be moved to googleurl. | 
 | namespace { | 
 |  | 
 | std::string CanonicalizeHost(absl::string_view host, | 
 |                              url::CanonHostInfo* host_info) { | 
 |   // Try to canonicalize the host. | 
 |   const url::Component raw_host_component(0, static_cast<int>(host.length())); | 
 |   std::string canon_host; | 
 |   url::StdStringCanonOutput canon_host_output(&canon_host); | 
 |   url::CanonicalizeHostVerbose(host.data(), raw_host_component, | 
 |                                &canon_host_output, host_info); | 
 |  | 
 |   if (host_info->out_host.is_nonempty() && | 
 |       host_info->family != url::CanonHostInfo::BROKEN) { | 
 |     // Success!  Assert that there's no extra garbage. | 
 |     canon_host_output.Complete(); | 
 |     QUICHE_DCHECK_EQ(host_info->out_host.len, | 
 |                      static_cast<int>(canon_host.length())); | 
 |   } else { | 
 |     // Empty host, or canonicalization failed.  We'll return empty. | 
 |     canon_host.clear(); | 
 |   } | 
 |  | 
 |   return canon_host; | 
 | } | 
 |  | 
 | bool IsHostCharAlphanumeric(char c) { | 
 |   // We can just check lowercase because uppercase characters have already been | 
 |   // normalized. | 
 |   return ((c >= 'a') && (c <= 'z')) || ((c >= '0') && (c <= '9')); | 
 | } | 
 |  | 
 | bool IsCanonicalizedHostCompliant(const std::string& host) { | 
 |   if (host.empty()) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   bool in_component = false; | 
 |   bool most_recent_component_started_alphanumeric = false; | 
 |  | 
 |   for (char c : host) { | 
 |     if (!in_component) { | 
 |       most_recent_component_started_alphanumeric = IsHostCharAlphanumeric(c); | 
 |       if (!most_recent_component_started_alphanumeric && (c != '-') && | 
 |           (c != '_')) { | 
 |         return false; | 
 |       } | 
 |       in_component = true; | 
 |     } else if (c == '.') { | 
 |       in_component = false; | 
 |     } else if (!IsHostCharAlphanumeric(c) && (c != '-') && (c != '_')) { | 
 |       return false; | 
 |     } | 
 |   } | 
 |  | 
 |   return most_recent_component_started_alphanumeric; | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | // static | 
 | bool QuicheHostnameUtils::IsValidSNI(absl::string_view sni) { | 
 |   // TODO(rtenneti): Support RFC2396 hostname. | 
 |   // NOTE: Microsoft does NOT enforce this spec, so if we throw away hostnames | 
 |   // based on the above spec, we may be losing some hostnames that windows | 
 |   // would consider valid. By far the most common hostname character NOT | 
 |   // accepted by the above spec is '_'. | 
 |   url::CanonHostInfo host_info; | 
 |   std::string canonicalized_host = CanonicalizeHost(sni, &host_info); | 
 |   return !host_info.IsIPAddress() && | 
 |          IsCanonicalizedHostCompliant(canonicalized_host); | 
 | } | 
 |  | 
 | // static | 
 | std::string QuicheHostnameUtils::NormalizeHostname(absl::string_view hostname) { | 
 |   url::CanonHostInfo host_info; | 
 |   std::string host = CanonicalizeHost(hostname, &host_info); | 
 |  | 
 |   // Walk backwards over the string, stopping at the first trailing dot. | 
 |   size_t host_end = host.length(); | 
 |   while (host_end != 0 && host[host_end - 1] == '.') { | 
 |     host_end--; | 
 |   } | 
 |  | 
 |   // Erase the trailing dots. | 
 |   if (host_end != host.length()) { | 
 |     host.erase(host_end, host.length() - host_end); | 
 |   } | 
 |  | 
 |   return host; | 
 | } | 
 |  | 
 | }  // namespace quiche |