Wait for settings in MASQUE

This is required to simultaneously support multiple versions of draft-ietf-masque-h3-datagram.

This CL also fixes a bug in MASQUE with IPv4.

PiperOrigin-RevId: 379545552
diff --git a/quic/core/http/quic_spdy_session.h b/quic/core/http/quic_spdy_session.h
index 214d1da..810297d 100644
--- a/quic/core/http/quic_spdy_session.h
+++ b/quic/core/http/quic_spdy_session.h
@@ -433,6 +433,9 @@
   // Indicates whether the HTTP/3 session supports WebTransport.
   bool SupportsWebTransport();
 
+  // Indicates whether both the peer and us support HTTP/3 Datagrams.
+  bool SupportsH3Datagram() { return h3_datagram_supported_; }
+
   // Indicates whether the HTTP/3 session will indicate WebTransport support to
   // the peer.
   bool WillNegotiateWebTransport();
@@ -445,7 +448,7 @@
   // until the SETTINGS are received.  Only works for HTTP/3.
   bool ShouldBufferRequestsUntilSettings() {
     return version().UsesHttp3() && perspective() == Perspective::IS_SERVER &&
-           WillNegotiateWebTransport();
+           ShouldNegotiateHttp3Datagram();
   }
 
   // Returns if the incoming bidirectional streams should process data.  This is
diff --git a/quic/masque/masque_client_session.cc b/quic/masque/masque_client_session.cc
index 6eee553..31edff1 100644
--- a/quic/masque/masque_client_session.cc
+++ b/quic/masque/masque_client_session.cc
@@ -233,6 +233,19 @@
   QuicSpdyClientSession::OnStreamClosed(stream_id);
 }
 
+bool MasqueClientSession::OnSettingsFrame(const SettingsFrame& frame) {
+  if (!QuicSpdyClientSession::OnSettingsFrame(frame)) {
+    QUIC_DLOG(ERROR) << "Failed to parse received settings";
+    return false;
+  }
+  if (!SupportsH3Datagram()) {
+    QUIC_DLOG(ERROR) << "Refusing to use MASQUE without HTTP/3 Datagrams";
+    return false;
+  }
+  owner_->OnSettingsReceived();
+  return true;
+}
+
 MasqueClientSession::ConnectUdpClientState::ConnectUdpClientState(
     QuicSpdyClientStream* stream,
     EncapsulatedClientSession* encapsulated_client_session,
diff --git a/quic/masque/masque_client_session.h b/quic/masque/masque_client_session.h
index c32a961..0b2a3dd 100644
--- a/quic/masque/masque_client_session.h
+++ b/quic/masque/masque_client_session.h
@@ -31,6 +31,9 @@
     // Notifies the owner that the client connection ID is no longer in use.
     virtual void UnregisterClientConnectionId(
         QuicConnectionId client_connection_id) = 0;
+
+    // Notifies the owner that a settings frame has been received.
+    virtual void OnSettingsReceived() = 0;
   };
   // Interface meant to be implemented by encapsulated client sessions, i.e.
   // the end-to-end QUIC client sessions that run inside MASQUE encapsulation.
@@ -75,6 +78,9 @@
                           ConnectionCloseSource source) override;
   void OnStreamClosed(QuicStreamId stream_id) override;
 
+  // From QuicSpdySession.
+  bool OnSettingsFrame(const SettingsFrame& frame) override;
+
   // Send encapsulated packet.
   void SendPacket(QuicConnectionId client_connection_id,
                   QuicConnectionId server_connection_id,
diff --git a/quic/masque/masque_epoll_client.cc b/quic/masque/masque_epoll_client.cc
index 5ad91cf..ec47520 100644
--- a/quic/masque/masque_epoll_client.cc
+++ b/quic/masque/masque_epoll_client.cc
@@ -81,6 +81,11 @@
     return nullptr;
   }
 
+  if (!masque_client->WaitUntilSettingsReceived()) {
+    QUIC_LOG(ERROR) << "Failed to receive settings";
+    return nullptr;
+  }
+
   if (masque_client->masque_mode() == MasqueMode::kLegacy) {
     // Construct the legacy mode init request.
     spdy::Http2HeaderBlock header_block;
@@ -114,6 +119,17 @@
   return masque_client;
 }
 
+void MasqueEpollClient::OnSettingsReceived() {
+  settings_received_ = true;
+}
+
+bool MasqueEpollClient::WaitUntilSettingsReceived() {
+  while (connected() && !settings_received_) {
+    network_helper()->RunEventLoop();
+  }
+  return connected() && settings_received_;
+}
+
 void MasqueEpollClient::UnregisterClientConnectionId(
     QuicConnectionId client_connection_id) {
   std::string body(client_connection_id.data(), client_connection_id.length());
diff --git a/quic/masque/masque_epoll_client.h b/quic/masque/masque_epoll_client.h
index 1db161f..2f5faca 100644
--- a/quic/masque/masque_epoll_client.h
+++ b/quic/masque/masque_epoll_client.h
@@ -35,6 +35,8 @@
   // Convenience accessor for the underlying connection ID.
   QuicConnectionId connection_id();
 
+  // From MasqueClientSession::Owner.
+  void OnSettingsReceived() override;
   // Send a MASQUE client connection ID unregister command to the server.
   void UnregisterClientConnectionId(
       QuicConnectionId client_connection_id) override;
@@ -50,12 +52,17 @@
                     std::unique_ptr<ProofVerifier> proof_verifier,
                     const std::string& authority);
 
+  // Wait synchronously until we receive the peer's settings. Returns whether
+  // they were received.
+  bool WaitUntilSettingsReceived();
+
   // Disallow copy and assign.
   MasqueEpollClient(const MasqueEpollClient&) = delete;
   MasqueEpollClient& operator=(const MasqueEpollClient&) = delete;
 
   MasqueMode masque_mode_;
   std::string authority_;
+  bool settings_received_ = false;
 };
 
 }  // namespace quic
diff --git a/quic/masque/masque_server_session.cc b/quic/masque/masque_server_session.cc
index 8a531ca..3c14fca 100644
--- a/quic/masque/masque_server_session.cc
+++ b/quic/masque/masque_server_session.cc
@@ -255,9 +255,12 @@
       QUIC_DLOG(ERROR) << "Socket creation failed";
       return CreateBackendErrorResponse("500", "Socket creation failed");
     }
-    QuicSocketAddress any_v6_address(QuicIpAddress::Any6(), 0);
+    QuicSocketAddress empty_address(QuicIpAddress::Any6(), 0);
+    if (target_server_address.host().IsIPv4()) {
+      empty_address = QuicSocketAddress(QuicIpAddress::Any4(), 0);
+    }
     QuicUdpSocketApi socket_api;
-    if (!socket_api.Bind(fd_wrapper.fd(), any_v6_address)) {
+    if (!socket_api.Bind(fd_wrapper.fd(), empty_address)) {
       QUIC_DLOG(ERROR) << "Socket bind failed";
       return CreateBackendErrorResponse("500", "Socket bind failed");
     }