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 namespace webrtc {
17 
SctpTransport(std::unique_ptr<cricket::SctpTransportInternal> internal)18 SctpTransport::SctpTransport(
19     std::unique_ptr<cricket::SctpTransportInternal> internal)
20     : owner_thread_(rtc::Thread::Current()),
21       info_(SctpTransportState::kNew),
22       internal_sctp_transport_(std::move(internal)) {
23   RTC_DCHECK(internal_sctp_transport_.get());
24   internal_sctp_transport_->SignalAssociationChangeCommunicationUp.connect(
25       this, &SctpTransport::OnAssociationChangeCommunicationUp);
26   // TODO(https://bugs.webrtc.org/10360): Add handlers for transport closing.
27 
28   if (dtls_transport_) {
29     UpdateInformation(SctpTransportState::kConnecting);
30   } else {
31     UpdateInformation(SctpTransportState::kNew);
32   }
33 }
34 
~SctpTransport()35 SctpTransport::~SctpTransport() {
36   // We depend on the network thread to call Clear() before dropping
37   // its last reference to this object.
38   RTC_DCHECK(owner_thread_->IsCurrent() || !internal_sctp_transport_);
39 }
40 
Information() const41 SctpTransportInformation SctpTransport::Information() const {
42   rtc::CritScope scope(&lock_);
43   return info_;
44 }
45 
RegisterObserver(SctpTransportObserverInterface * observer)46 void SctpTransport::RegisterObserver(SctpTransportObserverInterface* observer) {
47   RTC_DCHECK_RUN_ON(owner_thread_);
48   RTC_DCHECK(observer);
49   RTC_DCHECK(!observer_);
50   observer_ = observer;
51 }
52 
UnregisterObserver()53 void SctpTransport::UnregisterObserver() {
54   RTC_DCHECK_RUN_ON(owner_thread_);
55   observer_ = nullptr;
56 }
57 
dtls_transport() const58 rtc::scoped_refptr<DtlsTransportInterface> SctpTransport::dtls_transport()
59     const {
60   RTC_DCHECK_RUN_ON(owner_thread_);
61   return dtls_transport_;
62 }
63 
64 // Internal functions
Clear()65 void SctpTransport::Clear() {
66   RTC_DCHECK_RUN_ON(owner_thread_);
67   RTC_DCHECK(internal());
68   {
69     rtc::CritScope scope(&lock_);
70     // Note that we delete internal_sctp_transport_, but
71     // only drop the reference to dtls_transport_.
72     dtls_transport_ = nullptr;
73     internal_sctp_transport_ = nullptr;
74   }
75   UpdateInformation(SctpTransportState::kClosed);
76 }
77 
SetDtlsTransport(rtc::scoped_refptr<DtlsTransport> transport)78 void SctpTransport::SetDtlsTransport(
79     rtc::scoped_refptr<DtlsTransport> transport) {
80   RTC_DCHECK_RUN_ON(owner_thread_);
81   SctpTransportState next_state;
82   {
83     rtc::CritScope scope(&lock_);
84     next_state = info_.state();
85     dtls_transport_ = transport;
86     if (internal_sctp_transport_) {
87       if (transport) {
88         internal_sctp_transport_->SetDtlsTransport(transport->internal());
89         transport->internal()->SignalDtlsState.connect(
90             this, &SctpTransport::OnDtlsStateChange);
91         if (info_.state() == SctpTransportState::kNew) {
92           next_state = SctpTransportState::kConnecting;
93         }
94       } else {
95         internal_sctp_transport_->SetDtlsTransport(nullptr);
96       }
97     }
98   }
99   UpdateInformation(next_state);
100 }
101 
Start(int local_port,int remote_port,int max_message_size)102 void SctpTransport::Start(int local_port,
103                           int remote_port,
104                           int max_message_size) {
105   {
106     rtc::CritScope scope(&lock_);
107     // Record max message size on calling thread.
108     info_ = SctpTransportInformation(info_.state(), info_.dtls_transport(),
109                                      max_message_size, info_.MaxChannels());
110   }
111   if (owner_thread_->IsCurrent()) {
112     if (!internal()->Start(local_port, remote_port, max_message_size)) {
113       RTC_LOG(LS_ERROR) << "Failed to push down SCTP parameters, closing.";
114       UpdateInformation(SctpTransportState::kClosed);
115     }
116   } else {
117     owner_thread_->Invoke<void>(
118         RTC_FROM_HERE, rtc::Bind(&SctpTransport::Start, this, local_port,
119                                  remote_port, max_message_size));
120   }
121 }
122 
UpdateInformation(SctpTransportState state)123 void SctpTransport::UpdateInformation(SctpTransportState state) {
124   RTC_DCHECK_RUN_ON(owner_thread_);
125   bool must_send_update;
126   SctpTransportInformation info_copy(SctpTransportState::kNew);
127   {
128     rtc::CritScope scope(&lock_);
129     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     if (observer_ && must_send_update) {
140       info_copy = info_;
141     }
142   }
143   // We call the observer without holding the lock.
144   if (observer_ && must_send_update) {
145     observer_->OnStateChange(info_copy);
146   }
147 }
148 
OnAssociationChangeCommunicationUp()149 void SctpTransport::OnAssociationChangeCommunicationUp() {
150   RTC_DCHECK_RUN_ON(owner_thread_);
151   {
152     rtc::CritScope scope(&lock_);
153     RTC_DCHECK(internal_sctp_transport_);
154     if (internal_sctp_transport_->max_outbound_streams() &&
155         internal_sctp_transport_->max_inbound_streams()) {
156       int max_channels =
157           std::min(*(internal_sctp_transport_->max_outbound_streams()),
158                    *(internal_sctp_transport_->max_inbound_streams()));
159       // Record max channels.
160       info_ = SctpTransportInformation(info_.state(), info_.dtls_transport(),
161                                        info_.MaxMessageSize(), max_channels);
162     }
163   }
164   UpdateInformation(SctpTransportState::kConnected);
165 }
166 
OnDtlsStateChange(cricket::DtlsTransportInternal * transport,cricket::DtlsTransportState state)167 void SctpTransport::OnDtlsStateChange(cricket::DtlsTransportInternal* transport,
168                                       cricket::DtlsTransportState state) {
169   RTC_DCHECK_RUN_ON(owner_thread_);
170   RTC_CHECK(transport == dtls_transport_->internal());
171   if (state == cricket::DTLS_TRANSPORT_CLOSED ||
172       state == cricket::DTLS_TRANSPORT_FAILED) {
173     UpdateInformation(SctpTransportState::kClosed);
174     // TODO(http://bugs.webrtc.org/11090): Close all the data channels
175   }
176 }
177 
178 }  // namespace webrtc
179