1 /*
2  *  Copyright 2018 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #include "pc/sctp_transport.h"
12 
13 #include <algorithm>
14 #include <utility>
15 
16 #include "absl/types/optional.h"
17 #include "api/sequence_checker.h"
18 #include "rtc_base/checks.h"
19 #include "rtc_base/location.h"
20 #include "rtc_base/logging.h"
21 
22 namespace webrtc {
23 
SctpTransport(std::unique_ptr<cricket::SctpTransportInternal> internal)24 SctpTransport::SctpTransport(
25     std::unique_ptr<cricket::SctpTransportInternal> internal)
26     : owner_thread_(rtc::Thread::Current()),
27       info_(SctpTransportState::kNew),
28       internal_sctp_transport_(std::move(internal)) {
29   RTC_DCHECK(internal_sctp_transport_.get());
30   internal_sctp_transport_->SignalAssociationChangeCommunicationUp.connect(
31       this, &SctpTransport::OnAssociationChangeCommunicationUp);
32   // TODO(https://bugs.webrtc.org/10360): Add handlers for transport closing.
33 
34   if (dtls_transport_) {
35     UpdateInformation(SctpTransportState::kConnecting);
36   } else {
37     UpdateInformation(SctpTransportState::kNew);
38   }
39 }
40 
~SctpTransport()41 SctpTransport::~SctpTransport() {
42   // We depend on the network thread to call Clear() before dropping
43   // its last reference to this object.
44   RTC_DCHECK(owner_thread_->IsCurrent() || !internal_sctp_transport_);
45 }
46 
Information() const47 SctpTransportInformation SctpTransport::Information() const {
48   // TODO(tommi): Update PeerConnection::GetSctpTransport to hand out a proxy
49   // to the transport so that we can be sure that methods get called on the
50   // expected thread. Chromium currently calls this method from
51   // TransceiverStateSurfacer.
52   if (!owner_thread_->IsCurrent()) {
53     return owner_thread_->Invoke<SctpTransportInformation>(
54         RTC_FROM_HERE, [this] { return Information(); });
55   }
56   RTC_DCHECK_RUN_ON(owner_thread_);
57   return info_;
58 }
59 
RegisterObserver(SctpTransportObserverInterface * observer)60 void SctpTransport::RegisterObserver(SctpTransportObserverInterface* observer) {
61   RTC_DCHECK_RUN_ON(owner_thread_);
62   RTC_DCHECK(observer);
63   RTC_DCHECK(!observer_);
64   observer_ = observer;
65 }
66 
UnregisterObserver()67 void SctpTransport::UnregisterObserver() {
68   RTC_DCHECK_RUN_ON(owner_thread_);
69   observer_ = nullptr;
70 }
71 
dtls_transport() const72 rtc::scoped_refptr<DtlsTransportInterface> SctpTransport::dtls_transport()
73     const {
74   RTC_DCHECK_RUN_ON(owner_thread_);
75   return dtls_transport_;
76 }
77 
78 // Internal functions
Clear()79 void SctpTransport::Clear() {
80   RTC_DCHECK_RUN_ON(owner_thread_);
81   RTC_DCHECK(internal());
82   // Note that we delete internal_sctp_transport_, but
83   // only drop the reference to dtls_transport_.
84   dtls_transport_ = nullptr;
85   internal_sctp_transport_ = nullptr;
86   UpdateInformation(SctpTransportState::kClosed);
87 }
88 
SetDtlsTransport(rtc::scoped_refptr<DtlsTransport> transport)89 void SctpTransport::SetDtlsTransport(
90     rtc::scoped_refptr<DtlsTransport> transport) {
91   RTC_DCHECK_RUN_ON(owner_thread_);
92   SctpTransportState next_state = info_.state();
93   dtls_transport_ = transport;
94   if (internal_sctp_transport_) {
95     if (transport) {
96       internal_sctp_transport_->SetDtlsTransport(transport->internal());
97 
98       transport->internal()->SubscribeDtlsState(
99           [this](cricket::DtlsTransportInternal* transport,
100                  cricket::DtlsTransportState state) {
101             OnDtlsStateChange(transport, state);
102           });
103       if (info_.state() == SctpTransportState::kNew) {
104         next_state = SctpTransportState::kConnecting;
105       }
106     } else {
107       internal_sctp_transport_->SetDtlsTransport(nullptr);
108     }
109   }
110 
111   UpdateInformation(next_state);
112 }
113 
Start(int local_port,int remote_port,int max_message_size)114 void SctpTransport::Start(int local_port,
115                           int remote_port,
116                           int max_message_size) {
117   RTC_DCHECK_RUN_ON(owner_thread_);
118   info_ = SctpTransportInformation(info_.state(), info_.dtls_transport(),
119                                    max_message_size, info_.MaxChannels());
120 
121   if (!internal()->Start(local_port, remote_port, max_message_size)) {
122     RTC_LOG(LS_ERROR) << "Failed to push down SCTP parameters, closing.";
123     UpdateInformation(SctpTransportState::kClosed);
124   }
125 }
126 
UpdateInformation(SctpTransportState state)127 void SctpTransport::UpdateInformation(SctpTransportState state) {
128   RTC_DCHECK_RUN_ON(owner_thread_);
129   bool must_send_update = (state != info_.state());
130   // TODO(https://bugs.webrtc.org/10358): Update max channels from internal
131   // SCTP transport when available.
132   if (internal_sctp_transport_) {
133     info_ = SctpTransportInformation(
134         state, dtls_transport_, info_.MaxMessageSize(), info_.MaxChannels());
135   } else {
136     info_ = SctpTransportInformation(
137         state, dtls_transport_, info_.MaxMessageSize(), info_.MaxChannels());
138   }
139 
140   if (observer_ && must_send_update) {
141     observer_->OnStateChange(info_);
142   }
143 }
144 
OnAssociationChangeCommunicationUp()145 void SctpTransport::OnAssociationChangeCommunicationUp() {
146   RTC_DCHECK_RUN_ON(owner_thread_);
147   RTC_DCHECK(internal_sctp_transport_);
148   if (internal_sctp_transport_->max_outbound_streams() &&
149       internal_sctp_transport_->max_inbound_streams()) {
150     int max_channels =
151         std::min(*(internal_sctp_transport_->max_outbound_streams()),
152                  *(internal_sctp_transport_->max_inbound_streams()));
153     // Record max channels.
154     info_ = SctpTransportInformation(info_.state(), info_.dtls_transport(),
155                                      info_.MaxMessageSize(), max_channels);
156   }
157 
158   UpdateInformation(SctpTransportState::kConnected);
159 }
160 
OnDtlsStateChange(cricket::DtlsTransportInternal * transport,cricket::DtlsTransportState state)161 void SctpTransport::OnDtlsStateChange(cricket::DtlsTransportInternal* transport,
162                                       cricket::DtlsTransportState state) {
163   RTC_DCHECK_RUN_ON(owner_thread_);
164   RTC_CHECK(transport == dtls_transport_->internal());
165   if (state == cricket::DTLS_TRANSPORT_CLOSED ||
166       state == cricket::DTLS_TRANSPORT_FAILED) {
167     UpdateInformation(SctpTransportState::kClosed);
168     // TODO(http://bugs.webrtc.org/11090): Close all the data channels
169   }
170 }
171 
172 }  // namespace webrtc
173