Stop an SCTP connection when the DTLS transport closes.

This CL propagates a "closed" signal from DTLS up to the
SCTP section of the data channel controller, where it causes
closing of all open datachannels.

Bug: chromium:1030631, webrtc:10360
Change-Id: I88bb9e1aff5c25f330edfd092ef609d4fcc3a9f8
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/162206
Reviewed-by: Steve Anton <steveanton@webrtc.org>
Commit-Queue: Harald Alvestrand <hta@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#30099}
diff --git a/api/transport/data_channel_transport_interface.h b/api/transport/data_channel_transport_interface.h
index db53a5e..671deff 100644
--- a/api/transport/data_channel_transport_interface.h
+++ b/api/transport/data_channel_transport_interface.h
@@ -84,6 +84,11 @@
   // invoked again following send errors (eg. due to the transport being
   // temporarily blocked or unavailable).
   virtual void OnReadyToSend() = 0;
+
+  // Callback issued when the data channel becomes unusable (closed).
+  // TODO(https://crbug.com/webrtc/10360): Make pure virtual when all
+  // consumers updated.
+  virtual void OnTransportClosed() {}
 };
 
 // Transport for data channels.
diff --git a/media/sctp/sctp_transport.cc b/media/sctp/sctp_transport.cc
index 9f1e862..31489eb 100644
--- a/media/sctp/sctp_transport.cc
+++ b/media/sctp/sctp_transport.cc
@@ -662,6 +662,7 @@
   transport_->SignalWritableState.connect(this,
                                           &SctpTransport::OnWritableState);
   transport_->SignalReadPacket.connect(this, &SctpTransport::OnPacketRead);
+  transport_->SignalClosed.connect(this, &SctpTransport::OnClosed);
 }
 
 void SctpTransport::DisconnectTransportSignals() {
@@ -671,6 +672,7 @@
   }
   transport_->SignalWritableState.disconnect(this);
   transport_->SignalReadPacket.disconnect(this);
+  transport_->SignalClosed.disconnect(this);
 }
 
 bool SctpTransport::Connect() {
@@ -990,6 +992,10 @@
   }
 }
 
+void SctpTransport::OnClosed(rtc::PacketTransportInternal* transport) {
+  SignalClosedAbruptly();
+}
+
 void SctpTransport::OnSendThresholdCallback() {
   RTC_DCHECK_RUN_ON(network_thread_);
   if (partial_outgoing_message_.has_value()) {
diff --git a/media/sctp/sctp_transport.h b/media/sctp/sctp_transport.h
index 7337f01..d346cfc 100644
--- a/media/sctp/sctp_transport.h
+++ b/media/sctp/sctp_transport.h
@@ -164,6 +164,7 @@
                             size_t len,
                             const int64_t& packet_time_us,
                             int flags);
+  void OnClosed(rtc::PacketTransportInternal* transport);
 
   // Methods related to usrsctp callbacks.
   void OnSendThresholdCallback();
diff --git a/media/sctp/sctp_transport_internal.h b/media/sctp/sctp_transport_internal.h
index 378453a..b0e0e0f 100644
--- a/media/sctp/sctp_transport_internal.h
+++ b/media/sctp/sctp_transport_internal.h
@@ -134,6 +134,9 @@
   // Parameter is SID; fired when closing procedure is complete (both incoming
   // and outgoing streams reset).
   sigslot::signal1<int> SignalClosingProcedureComplete;
+  // Fired when the underlying DTLS transport has closed due to an error
+  // or an incoming DTLS disconnect.
+  sigslot::signal0<> SignalClosedAbruptly;
 
   // Helper for debugging.
   virtual void set_debug_name_for_testing(const char* debug_name) = 0;
diff --git a/p2p/base/dtls_transport.cc b/p2p/base/dtls_transport.cc
index acd5765..538aa86 100644
--- a/p2p/base/dtls_transport.cc
+++ b/p2p/base/dtls_transport.cc
@@ -656,6 +656,7 @@
         RTC_LOG(LS_INFO) << ToString() << ": DTLS transport closed by remote";
         set_writable(false);
         set_dtls_state(DTLS_TRANSPORT_CLOSED);
+        SignalClosed(this);
       } else if (ret == rtc::SR_ERROR) {
         // Remote peer shut down the association with an error.
         RTC_LOG(LS_INFO)
@@ -664,6 +665,7 @@
             << read_error;
         set_writable(false);
         set_dtls_state(DTLS_TRANSPORT_FAILED);
+        SignalClosed(this);
       }
     } while (ret == rtc::SR_SUCCESS);
   }
diff --git a/p2p/base/packet_transport_internal.h b/p2p/base/packet_transport_internal.h
index a532183..f65d7f4 100644
--- a/p2p/base/packet_transport_internal.h
+++ b/p2p/base/packet_transport_internal.h
@@ -95,6 +95,9 @@
   // Signalled when the current network route has changed.
   sigslot::signal1<absl::optional<rtc::NetworkRoute>> SignalNetworkRouteChanged;
 
+  // Signalled when the transport is closed.
+  sigslot::signal1<PacketTransportInternal*> SignalClosed;
+
  protected:
   PacketTransportInternal();
   ~PacketTransportInternal() override;
diff --git a/pc/data_channel_controller.cc b/pc/data_channel_controller.cc
index cb93384..2800992 100644
--- a/pc/data_channel_controller.cc
+++ b/pc/data_channel_controller.cc
@@ -181,6 +181,15 @@
       });
 }
 
+void DataChannelController::OnTransportClosed() {
+  RTC_DCHECK_RUN_ON(network_thread());
+  data_channel_transport_invoker_->AsyncInvoke<void>(
+      RTC_FROM_HERE, signaling_thread(), [this] {
+        RTC_DCHECK_RUN_ON(signaling_thread());
+        OnTransportChannelClosed();
+      });
+}
+
 void DataChannelController::SetupDataChannelTransport_n() {
   RTC_DCHECK_RUN_ON(network_thread());
   data_channel_transport_invoker_ = std::make_unique<rtc::AsyncInvoker>();
diff --git a/pc/data_channel_controller.h b/pc/data_channel_controller.h
index 91bba66..5e00259 100644
--- a/pc/data_channel_controller.h
+++ b/pc/data_channel_controller.h
@@ -46,6 +46,7 @@
   void OnChannelClosing(int channel_id) override;
   void OnChannelClosed(int channel_id) override;
   void OnReadyToSend() override;
+  void OnTransportClosed() override;
 
   // Called from PeerConnection::SetupDataChannelTransport_n
   void SetupDataChannelTransport_n();
diff --git a/pc/peer_connection_integrationtest.cc b/pc/peer_connection_integrationtest.cc
index 7a8d152..58f5aa6 100644
--- a/pc/peer_connection_integrationtest.cc
+++ b/pc/peer_connection_integrationtest.cc
@@ -5989,6 +5989,44 @@
   ASSERT_TRUE(caller()->data_observer()->IsOpen());
 }
 
+TEST_F(PeerConnectionIntegrationTestUnifiedPlan, DataChannelClosesWhenClosed) {
+  ASSERT_TRUE(CreatePeerConnectionWrappers());
+  ConnectFakeSignaling();
+  caller()->CreateDataChannel();
+  caller()->CreateAndSetAndSignalOffer();
+  ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+  ASSERT_TRUE_WAIT(callee()->data_observer(), kDefaultTimeout);
+  ASSERT_TRUE_WAIT(callee()->data_observer()->IsOpen(), kDefaultTimeout);
+  caller()->data_channel()->Close();
+  ASSERT_TRUE_WAIT(!callee()->data_observer()->IsOpen(), kDefaultTimeout);
+}
+
+TEST_F(PeerConnectionIntegrationTestUnifiedPlan,
+       DataChannelClosesWhenClosedReverse) {
+  ASSERT_TRUE(CreatePeerConnectionWrappers());
+  ConnectFakeSignaling();
+  caller()->CreateDataChannel();
+  caller()->CreateAndSetAndSignalOffer();
+  ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+  ASSERT_TRUE_WAIT(callee()->data_observer(), kDefaultTimeout);
+  ASSERT_TRUE_WAIT(callee()->data_observer()->IsOpen(), kDefaultTimeout);
+  callee()->data_channel()->Close();
+  ASSERT_TRUE_WAIT(!caller()->data_observer()->IsOpen(), kDefaultTimeout);
+}
+
+TEST_F(PeerConnectionIntegrationTestUnifiedPlan,
+       DataChannelClosesWhenPeerConnectionClosed) {
+  ASSERT_TRUE(CreatePeerConnectionWrappers());
+  ConnectFakeSignaling();
+  caller()->CreateDataChannel();
+  caller()->CreateAndSetAndSignalOffer();
+  ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout);
+  ASSERT_TRUE_WAIT(callee()->data_observer(), kDefaultTimeout);
+  ASSERT_TRUE_WAIT(callee()->data_observer()->IsOpen(), kDefaultTimeout);
+  caller()->pc()->Close();
+  ASSERT_TRUE_WAIT(!callee()->data_observer()->IsOpen(), kDefaultTimeout);
+}
+
 #endif  // HAVE_SCTP
 
 }  // namespace
diff --git a/pc/sctp_data_channel_transport.cc b/pc/sctp_data_channel_transport.cc
index d1505f3..497e11f 100644
--- a/pc/sctp_data_channel_transport.cc
+++ b/pc/sctp_data_channel_transport.cc
@@ -24,6 +24,8 @@
       this, &SctpDataChannelTransport::OnClosingProcedureStartedRemotely);
   sctp_transport_->SignalClosingProcedureComplete.connect(
       this, &SctpDataChannelTransport::OnClosingProcedureComplete);
+  sctp_transport_->SignalClosedAbruptly.connect(
+      this, &SctpDataChannelTransport::OnClosedAbruptly);
 }
 
 RTCError SctpDataChannelTransport::OpenChannel(int channel_id) {
@@ -109,4 +111,10 @@
   }
 }
 
+void SctpDataChannelTransport::OnClosedAbruptly() {
+  if (sink_) {
+    sink_->OnTransportClosed();
+  }
+}
+
 }  // namespace webrtc
diff --git a/pc/sctp_data_channel_transport.h b/pc/sctp_data_channel_transport.h
index 281c30e..623a490 100644
--- a/pc/sctp_data_channel_transport.h
+++ b/pc/sctp_data_channel_transport.h
@@ -38,6 +38,7 @@
                       const rtc::CopyOnWriteBuffer& buffer);
   void OnClosingProcedureStartedRemotely(int channel_id);
   void OnClosingProcedureComplete(int channel_id);
+  void OnClosedAbruptly();
 
   cricket::SctpTransportInternal* const sctp_transport_;