Defer the creation of control streams in QBONE client & server until encryption is established. Default enabled by --qbone_client_defer_control_stream_creation & --qbone_server_defer_control_stream_creation.

This should be a no-op as a sane client/server should only be able to write/read(decrypt) control stream related frames only after encryption is established. And receiving any frame related to the control stream earlier than that should result in connection close.

PiperOrigin-RevId: 413473872
diff --git a/quic/qbone/qbone_client_session.cc b/quic/qbone/qbone_client_session.cc
index 1f8ca0a..fce08d2 100644
--- a/quic/qbone/qbone_client_session.cc
+++ b/quic/qbone/qbone_client_session.cc
@@ -10,6 +10,11 @@
 #include "quic/core/quic_types.h"
 #include "quic/qbone/qbone_constants.h"
 
+DEFINE_QUIC_COMMAND_LINE_FLAG(
+    bool, qbone_client_defer_control_stream_creation, true,
+    "If true, control stream in QBONE client session is created after "
+    "encryption established.");
+
 namespace quic {
 
 QboneClientSession::QboneClientSession(
@@ -34,12 +39,10 @@
       /*has_application_state = */ true);
 }
 
-void QboneClientSession::Initialize() {
-  // Initialize must be called first, as that's what generates the crypto
-  // stream.
-  QboneSessionBase::Initialize();
-  static_cast<QuicCryptoClientStreamBase*>(GetMutableCryptoStream())
-      ->CryptoConnect();
+void QboneClientSession::CreateControlStream() {
+  if (control_stream_ != nullptr) {
+    return;
+  }
   // Register the reserved control stream.
   QuicStreamId next_id = GetNextOutgoingBidirectionalStreamId();
   QUICHE_DCHECK_EQ(next_id,
@@ -50,6 +53,26 @@
   ActivateStream(std::move(control_stream));
 }
 
+void QboneClientSession::Initialize() {
+  // Initialize must be called first, as that's what generates the crypto
+  // stream.
+  QboneSessionBase::Initialize();
+  static_cast<QuicCryptoClientStreamBase*>(GetMutableCryptoStream())
+      ->CryptoConnect();
+  if (!GetQuicFlag(FLAGS_qbone_client_defer_control_stream_creation)) {
+    CreateControlStream();
+  }
+}
+
+void QboneClientSession::SetDefaultEncryptionLevel(
+    quic::EncryptionLevel level) {
+  QboneSessionBase::SetDefaultEncryptionLevel(level);
+  if (GetQuicFlag(FLAGS_qbone_client_defer_control_stream_creation) &&
+      level == quic::ENCRYPTION_FORWARD_SECURE) {
+    CreateControlStream();
+  }
+}
+
 int QboneClientSession::GetNumSentClientHellos() const {
   return static_cast<const QuicCryptoClientStreamBase*>(GetCryptoStream())
       ->num_sent_client_hellos();
diff --git a/quic/qbone/qbone_client_session.h b/quic/qbone/qbone_client_session.h
index 442b859..8f1b3a9 100644
--- a/quic/qbone/qbone_client_session.h
+++ b/quic/qbone/qbone_client_session.h
@@ -33,6 +33,8 @@
 
   // QuicSession overrides. This will initiate the crypto stream.
   void Initialize() override;
+  // Override to create control stream at FORWARD_SECURE encryption level.
+  void SetDefaultEncryptionLevel(quic::EncryptionLevel level) override;
 
   // Returns the number of client hello messages that have been sent on the
   // crypto stream. If the handshake has completed then this is one greater
@@ -65,6 +67,9 @@
   // QboneSessionBase interface implementation.
   std::unique_ptr<QuicCryptoStream> CreateCryptoStream() override;
 
+  // Instantiate QboneClientControlStream.
+  void CreateControlStream();
+
   // ProofHandler interface implementation.
   void OnProofValid(const QuicCryptoClientConfig::CachedState& cached) override;
   void OnProofVerifyDetailsAvailable(
@@ -82,7 +87,7 @@
   // Passed to the control stream.
   QboneClientControlStream::Handler* handler_;
   // The unowned control stream.
-  QboneClientControlStream* control_stream_;
+  QboneClientControlStream* control_stream_ = nullptr;
 };
 
 }  // namespace quic
diff --git a/quic/qbone/qbone_server_session.cc b/quic/qbone/qbone_server_session.cc
index 3b80dab..2ddb8e4 100644
--- a/quic/qbone/qbone_server_session.cc
+++ b/quic/qbone/qbone_server_session.cc
@@ -4,6 +4,7 @@
 
 #include "quic/qbone/qbone_server_session.h"
 
+#include <string>
 #include <utility>
 
 #include "absl/strings/string_view.h"
@@ -12,6 +13,11 @@
 #include "quic/core/quic_utils.h"
 #include "quic/qbone/qbone_constants.h"
 
+DEFINE_QUIC_COMMAND_LINE_FLAG(
+    bool, qbone_server_defer_control_stream_creation, true,
+    "If true, control stream in QBONE server session is created after "
+    "encryption established.");
+
 namespace quic {
 
 bool QboneCryptoServerStreamHelper::CanAcceptClientHello(
@@ -55,8 +61,10 @@
                                   &stream_helper_);
 }
 
-void QboneServerSession::Initialize() {
-  QboneSessionBase::Initialize();
+void QboneServerSession::CreateControlStream() {
+  if (control_stream_ != nullptr) {
+    return;
+  }
   // Register the reserved control stream.
   auto control_stream =
       std::make_unique<QboneServerControlStream>(this, handler_);
@@ -64,6 +72,22 @@
   ActivateStream(std::move(control_stream));
 }
 
+void QboneServerSession::Initialize() {
+  QboneSessionBase::Initialize();
+  if (!GetQuicFlag(FLAGS_qbone_server_defer_control_stream_creation)) {
+    CreateControlStream();
+  }
+}
+
+void QboneServerSession::SetDefaultEncryptionLevel(
+    quic::EncryptionLevel level) {
+  QboneSessionBase::SetDefaultEncryptionLevel(level);
+  if (GetQuicFlag(FLAGS_qbone_server_defer_control_stream_creation) &&
+      level == quic::ENCRYPTION_FORWARD_SECURE) {
+    CreateControlStream();
+  }
+}
+
 bool QboneServerSession::SendClientRequest(const QboneClientRequest& request) {
   if (!control_stream_) {
     QUIC_BUG(quic_bug_11026_1)
diff --git a/quic/qbone/qbone_server_session.h b/quic/qbone/qbone_server_session.h
index 76b7333..f5af8b4 100644
--- a/quic/qbone/qbone_server_session.h
+++ b/quic/qbone/qbone_server_session.h
@@ -50,6 +50,8 @@
   ~QboneServerSession() override;
 
   void Initialize() override;
+  // Override to create control stream at FORWARD_SECURE encryption level.
+  void SetDefaultEncryptionLevel(quic::EncryptionLevel level) override;
 
   virtual bool SendClientRequest(const QboneClientRequest& request);
 
@@ -73,6 +75,10 @@
  protected:
   // QboneSessionBase interface implementation.
   std::unique_ptr<QuicCryptoStream> CreateCryptoStream() override;
+
+  // Instantiate QboneServerControlStream.
+  void CreateControlStream();
+
   // The packet processor.
   QbonePacketProcessor processor_;
 
@@ -86,7 +92,7 @@
   // Passed to the control stream.
   QboneServerControlStream::Handler* handler_;
   // The unowned control stream.
-  QboneServerControlStream* control_stream_;
+  QboneServerControlStream* control_stream_ = nullptr;
 };
 
 }  // namespace quic