Allow SNIs without dots

This CL modifies GFE behavior when receiving a TLS handshake containing an SNI value with no dots in it.  The current behavior is that GFE will ignore such values and behave as though no SNI value had been passed at all.  Now, GFE will treat these as valid SNIs and perform cert lookup based on them.

Note that this CL hides cert-regeneration changes behind a diffbase.

Protected by gfe2_reloadable_flag_tls_allow_sni_without_dots.

PiperOrigin-RevId: 363489827
Change-Id: I61b1ea6fdca1e02ad0cecb041f3e09352e0823fd
diff --git a/quic/core/crypto/crypto_server_test.cc b/quic/core/crypto/crypto_server_test.cc
index 323aec1..7414eda 100644
--- a/quic/core/crypto/crypto_server_test.cc
+++ b/quic/core/crypto/crypto_server_test.cc
@@ -12,6 +12,7 @@
 
 #include "absl/base/macros.h"
 #include "absl/strings/escaping.h"
+#include "absl/strings/str_cat.h"
 #include "absl/strings/string_view.h"
 #include "third_party/boringssl/src/include/openssl/sha.h"
 #include "quic/core/crypto/cert_compressor.h"
@@ -57,24 +58,23 @@
 }  // namespace
 
 struct TestParams {
-  TestParams(ParsedQuicVersionVector supported_versions)
-      : supported_versions(std::move(supported_versions)) {}
-
   friend std::ostream& operator<<(std::ostream& os, const TestParams& p) {
     os << "  versions: "
-       << ParsedQuicVersionVectorToString(p.supported_versions) << " }";
+       << ParsedQuicVersionVectorToString(p.supported_versions)
+       << " } allow_sni_without_dots: " << p.allow_sni_without_dots;
     return os;
   }
 
   // Versions supported by client and server.
   ParsedQuicVersionVector supported_versions;
+  bool allow_sni_without_dots;
 };
 
 // Used by ::testing::PrintToStringParamName().
 std::string PrintToString(const TestParams& p) {
   std::string rv = ParsedQuicVersionVectorToString(p.supported_versions);
   std::replace(rv.begin(), rv.end(), ',', '_');
-  return rv;
+  return absl::StrCat(rv, "_allow_sni_without_dots_", p.allow_sni_without_dots);
 }
 
 // Constructs various test permutations.
@@ -84,7 +84,9 @@
   // Start with all versions, remove highest on each iteration.
   ParsedQuicVersionVector supported_versions = AllSupportedVersions();
   while (!supported_versions.empty()) {
-    params.push_back(TestParams(supported_versions));
+    for (bool allow_sni_without_dots : {false, true}) {
+      params.push_back({supported_versions, allow_sni_without_dots});
+    }
     supported_versions.erase(supported_versions.begin());
   }
 
@@ -108,6 +110,8 @@
         signed_config_(new QuicSignedServerConfig),
         chlo_packet_size_(kDefaultMaxPacketSize) {
     supported_versions_ = GetParam().supported_versions;
+    SetQuicReloadableFlag(quic_and_tls_allow_sni_without_dots,
+                          GetParam().allow_sni_without_dots);
     config_.set_enable_serving_sct(true);
 
     client_version_ = supported_versions_.front();
@@ -377,27 +381,34 @@
 
 TEST_P(CryptoServerTest, BadSNI) {
   // clang-format off
-  static const char* const kBadSNIs[] = {
+  std::vector<std::string> badSNIs = {
     "",
-    "foo",
     "#00",
     "#ff00",
     "127.0.0.1",
     "ffee::1",
   };
+  if (!GetParam().allow_sni_without_dots) {
+    badSNIs.push_back("foo");
+  }
   // clang-format on
 
-  for (size_t i = 0; i < ABSL_ARRAYSIZE(kBadSNIs); i++) {
-    CryptoHandshakeMessage msg =
-        crypto_test_utils::CreateCHLO({{"PDMD", "X509"},
-                                       {"SNI", kBadSNIs[i]},
-                                       {"VER\0", client_version_string_}},
-                                      kClientHelloMinimumSize);
+  for (const std::string& bad_sni : badSNIs) {
+    CryptoHandshakeMessage msg = crypto_test_utils::CreateCHLO(
+        {{"PDMD", "X509"}, {"SNI", bad_sni}, {"VER\0", client_version_string_}},
+        kClientHelloMinimumSize);
     ShouldFailMentioning("SNI", msg);
     const HandshakeFailureReason kRejectReasons[] = {
         SERVER_CONFIG_INCHOATE_HELLO_FAILURE};
     CheckRejectReasons(kRejectReasons, ABSL_ARRAYSIZE(kRejectReasons));
   }
+
+  if (GetParam().allow_sni_without_dots) {
+    CryptoHandshakeMessage msg = crypto_test_utils::CreateCHLO(
+        {{"PDMD", "X509"}, {"SNI", "foo"}, {"VER\0", client_version_string_}},
+        kClientHelloMinimumSize);
+    ShouldSucceed(msg);
+  }
 }
 
 TEST_P(CryptoServerTest, DefaultCert) {
diff --git a/quic/core/quic_flags_list.h b/quic/core/quic_flags_list.h
index 8beff10..f2f803d 100644
--- a/quic/core/quic_flags_list.h
+++ b/quic/core/quic_flags_list.h
@@ -11,6 +11,7 @@
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_allocate_stream_sequencer_buffer_blocks_on_demand, false)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_allow_client_enabled_bbr_v2, false)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_alps_include_scheme_in_origin, false)
+QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_and_tls_allow_sni_without_dots, false)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_avoid_too_low_probe_bw_cwnd, false)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_bbr2_fewer_startup_round_trips, true)
 QUIC_FLAG(FLAGS_quic_reloadable_flag_quic_can_send_ack_frequency, true)
diff --git a/quic/platform/api/quic_hostname_utils_test.cc b/quic/platform/api/quic_hostname_utils_test.cc
index 7049715..788cb2d 100644
--- a/quic/platform/api/quic_hostname_utils_test.cc
+++ b/quic/platform/api/quic_hostname_utils_test.cc
@@ -19,6 +19,9 @@
   // IP as SNI.
   EXPECT_FALSE(QuicHostnameUtils::IsValidSNI("192.168.0.1"));
   // SNI without any dot.
+  SetQuicReloadableFlag(quic_and_tls_allow_sni_without_dots, true);
+  EXPECT_TRUE(QuicHostnameUtils::IsValidSNI("somedomain"));
+  SetQuicReloadableFlag(quic_and_tls_allow_sni_without_dots, false);
   EXPECT_FALSE(QuicHostnameUtils::IsValidSNI("somedomain"));
   // Invalid by RFC2396 but unfortunately domains of this form exist.
   EXPECT_TRUE(QuicHostnameUtils::IsValidSNI("some_domain.com"));