Bug Summary

File:var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp
Warning:line 2479, column 5
Method called on moved-from object 'mPending' of type 'std::set'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name DataChannel.cpp -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=cplusplus -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -analyzer-config-compatibility-mode=true -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -ffp-contract=off -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fdebug-compilation-dir=/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/netwerk/sctp/datachannel -fcoverage-compilation-dir=/var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/netwerk/sctp/datachannel -resource-dir /usr/lib/llvm-19/lib/clang/19 -include /var/lib/jenkins/workspace/firefox-scan-build/config/gcc_hidden.h -include /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/mozilla-config.h -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/stl_wrappers -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/system_wrappers -U _FORTIFY_SOURCE -D _FORTIFY_SOURCE=2 -D DEBUG=1 -D SCTP_DEBUG=1 -D __Userspace_os_Linux=1 -D MOZ_HAS_MOZGLUE -D MOZILLA_INTERNAL_API -D IMPL_LIBXUL -D MOZ_SUPPORT_LEAKCHECKING -D STATIC_EXPORTABLE_JS_API -I /var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/netwerk/sctp/datachannel -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/ipc/ipdl/_ipdlheaders -I /var/lib/jenkins/workspace/firefox-scan-build/ipc/chromium/src -I /var/lib/jenkins/workspace/firefox-scan-build/dom/media/webrtc -I /var/lib/jenkins/workspace/firefox-scan-build/dom/media/webrtc/transport -I /var/lib/jenkins/workspace/firefox-scan-build/media/webrtc -I /var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/src -I /var/lib/jenkins/workspace/firefox-scan-build/third_party/libwebrtc -I /var/lib/jenkins/workspace/firefox-scan-build/third_party/libwebrtc/third_party/abseil-cpp -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/nspr -I /var/lib/jenkins/workspace/firefox-scan-build/obj-x86_64-pc-linux-gnu/dist/include/nss -D MOZILLA_CLIENT -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/x86_64-linux-gnu/c++/14 -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../include/c++/14/backward -internal-isystem /usr/lib/llvm-19/lib/clang/19/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -Wno-error=tautological-type-limit-compare -Wno-invalid-offsetof -Wno-range-loop-analysis -Wno-deprecated-anon-enum-enum-conversion -Wno-deprecated-enum-enum-conversion -Wno-deprecated-this-capture -Wno-inline-new-delete -Wno-error=deprecated-declarations -Wno-error=array-bounds -Wno-error=free-nonheap-object -Wno-error=atomic-alignment -Wno-error=deprecated-builtins -Wno-psabi -Wno-error=builtin-macro-redefined -Wno-vla-cxx-extension -Wno-unknown-warning-option -fdeprecated-macro -ferror-limit 19 -fstrict-flex-arrays=1 -stack-protector 2 -fstack-clash-protection -ftrivial-auto-var-init=pattern -fno-rtti -fgnuc-version=4.2.1 -fskip-odr-check-in-gmf -fno-sized-deallocation -fno-aligned-allocation -vectorize-loops -vectorize-slp -analyzer-checker optin.performance.Padding -analyzer-output=html -analyzer-config stable-report-filename=true -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /tmp/scan-build-2024-09-22-115206-3586786-1 -x c++ /var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp
1/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2/* vim: set ts=2 et sw=2 tw=80: */
3/* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7#include <algorithm>
8#include <stdio.h>
9#include <stdlib.h>
10#if !defined(__Userspace_os_Windows)
11# include <arpa/inet.h>
12#endif
13// usrsctp.h expects to have errno definitions prior to its inclusion.
14#include <errno(*__errno_location ()).h>
15
16#define SCTP_DEBUG1 1
17#define SCTP_STDINT_INCLUDE<stdint.h> <stdint.h>
18
19#ifdef _MSC_VER
20// Disable "warning C4200: nonstandard extension used : zero-sized array in
21// struct/union"
22// ...which the third-party file usrsctp.h runs afoul of.
23# pragma warning(push)
24# pragma warning(disable : 4200)
25#endif
26
27#include "usrsctp.h"
28
29#ifdef _MSC_VER
30# pragma warning(pop)
31#endif
32
33#include "nsIInputStream.h"
34#include "nsIPrefBranch.h"
35#include "nsIPrefService.h"
36#include "mozilla/Sprintf.h"
37#include "nsProxyRelease.h"
38#include "nsThread.h"
39#include "nsThreadUtils.h"
40#include "nsNetUtil.h"
41#include "mozilla/Components.h"
42#include "mozilla/StaticMutex.h"
43#include "mozilla/UniquePtrExtensions.h"
44#include "mozilla/Unused.h"
45#include "mozilla/dom/RTCDataChannelBinding.h"
46#include "mozilla/dom/RTCStatsReportBinding.h"
47#include "mozilla/media/MediaUtils.h"
48#ifdef MOZ_PEERCONNECTION1
49# include "transport/runnable_utils.h"
50# include "jsapi/MediaTransportHandler.h"
51# include "mediapacket.h"
52#endif
53
54#include "DataChannel.h"
55#include "DataChannelLog.h"
56#include "DataChannelProtocol.h"
57
58// Let us turn on and off important assertions in non-debug builds
59#ifdef DEBUG1
60# define ASSERT_WEBRTC(x)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((x))>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!((x)))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("(x)", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 60); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(x)" ")"); do
{ *((volatile int*)__null) = 60; __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
MOZ_ASSERT((x))do { static_assert( mozilla::detail::AssertionConditionType<
decltype((x))>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!((x)))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("(x)", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 60); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(x)" ")"); do
{ *((volatile int*)__null) = 60; __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
61#elif defined(MOZ_WEBRTC_ASSERT_ALWAYS1)
62# define ASSERT_WEBRTC(x)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((x))>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!((x)))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("(x)", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 62); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(x)" ")"); do
{ *((volatile int*)__null) = 62; __attribute__((nomerge)) ::
abort(); } while (false); } } while (false)
\
63 do { \
64 if (!(x)) { \
65 MOZ_CRASH()do { do { } while (false); MOZ_ReportCrash("" , "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 65); AnnotateMozCrashReason("MOZ_CRASH(" ")"); do { *((volatile
int*)__null) = 65; __attribute__((nomerge)) ::abort(); } while
(false); } while (false)
; \
66 } \
67 } while (0)
68#endif
69
70namespace mozilla {
71
72LazyLogModule gDataChannelLog("DataChannel");
73static LazyLogModule gSCTPLog("SCTP");
74
75#define SCTP_LOG(args)do { const ::mozilla::LogModule* moz_real_module = mozilla::gSCTPLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, MOZ_LOG_EXPAND_ARGS
args); } } while (0)
\
76 MOZ_LOG(mozilla::gSCTPLog, mozilla::LogLevel::Debug, args)do { const ::mozilla::LogModule* moz_real_module = mozilla::gSCTPLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, MOZ_LOG_EXPAND_ARGS
args); } } while (0)
77
78static void debug_printf(const char* format, ...) {
79 va_list ap;
80 char buffer[1024];
81
82 if (MOZ_LOG_TEST(gSCTPLog, LogLevel::Debug)(__builtin_expect(!!(mozilla::detail::log_test(gSCTPLog, LogLevel
::Debug)), 0))
) {
83 va_start(ap, format)__builtin_va_start(ap, format);
84#ifdef _WIN32
85 if (vsnprintf_s(buffer, sizeof(buffer), _TRUNCATE, format, ap) > 0) {
86#else
87 if (VsprintfLiteral(buffer, format, ap) > 0) {
88#endif
89 SCTP_LOG(("%s", buffer))do { const ::mozilla::LogModule* moz_real_module = mozilla::gSCTPLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "%s", buffer); } }
while (0)
;
90 }
91 va_end(ap)__builtin_va_end(ap);
92 }
93}
94
95static constexpr const char* ToString(DataChannelState state) {
96 switch (state) {
97 case DataChannelState::Connecting:
98 return "CONNECTING";
99 case DataChannelState::Open:
100 return "OPEN";
101 case DataChannelState::Closing:
102 return "CLOSING";
103 case DataChannelState::Closed:
104 return "CLOSED";
105 }
106 return "";
107};
108
109static constexpr const char* ToString(DataChannelConnectionState state) {
110 switch (state) {
111 case DataChannelConnectionState::Connecting:
112 return "CONNECTING";
113 case DataChannelConnectionState::Open:
114 return "OPEN";
115 case DataChannelConnectionState::Closed:
116 return "CLOSED";
117 }
118 return "";
119};
120
121static constexpr const char* ToString(
122 DataChannelOnMessageAvailable::EventType type) {
123 switch (type) {
124 case DataChannelOnMessageAvailable::EventType::OnConnection:
125 return "ON_CONNECTION";
126 case DataChannelOnMessageAvailable::EventType::OnDisconnected:
127 return "ON_DISCONNECTED";
128 case DataChannelOnMessageAvailable::EventType::OnChannelCreated:
129 return "ON_CHANNEL_CREATED";
130 case DataChannelOnMessageAvailable::EventType::OnDataString:
131 return "ON_DATA_STRING";
132 case DataChannelOnMessageAvailable::EventType::OnDataBinary:
133 return "ON_DATA_BINARY";
134 }
135 return "";
136};
137
138static constexpr const char* ToString(DataChannelConnection::PendingType type) {
139 switch (type) {
140 case DataChannelConnection::PendingType::None:
141 return "NONE";
142 case DataChannelConnection::PendingType::Dcep:
143 return "DCEP";
144 case DataChannelConnection::PendingType::Data:
145 return "DATA";
146 }
147 return "";
148};
149
150static constexpr const char* ToString(DataChannelReliabilityPolicy type) {
151 switch (type) {
152 case DataChannelReliabilityPolicy::Reliable:
153 return "RELIABLE";
154 case DataChannelReliabilityPolicy::LimitedRetransmissions:
155 return "LIMITED_RETRANSMISSIONS";
156 case DataChannelReliabilityPolicy::LimitedLifetime:
157 return "LIMITED_LIFETIME";
158 }
159 return "";
160};
161
162static constexpr uint16_t ToUsrsctpValue(DataChannelReliabilityPolicy type) {
163 switch (type) {
164 case DataChannelReliabilityPolicy::Reliable:
165 return SCTP_PR_SCTP_NONE0x0000;
166 case DataChannelReliabilityPolicy::LimitedRetransmissions:
167 return SCTP_PR_SCTP_RTX0x0003;
168 case DataChannelReliabilityPolicy::LimitedLifetime:
169 return SCTP_PR_SCTP_TTL0x0001;
170 }
171 return SCTP_PR_SCTP_NONE0x0000;
172};
173
174class DataChannelRegistry {
175 public:
176 static uintptr_t Register(DataChannelConnection* aConnection) {
177 StaticMutexAutoLock lock(sInstanceMutex);
178 uintptr_t result = EnsureInstance()->RegisterImpl(aConnection);
179 DC_DEBUG(do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Registering connection %p as ulp %p"
, aConnection, (void*)result); } } while (0)
180 ("Registering connection %p as ulp %p", aConnection, (void*)result))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Registering connection %p as ulp %p"
, aConnection, (void*)result); } } while (0)
;
181 return result;
182 }
183
184 static void Deregister(uintptr_t aId) {
185 std::unique_ptr<DataChannelRegistry> maybeTrash;
186
187 {
188 StaticMutexAutoLock lock(sInstanceMutex);
189 DC_DEBUG(("Deregistering connection ulp = %p", (void*)aId))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Deregistering connection ulp = %p"
, (void*)aId); } } while (0)
;
190 if (NS_WARN_IF(!Instance())NS_warn_if_impl(!Instance(), "!Instance()", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 190)
) {
191 return;
192 }
193 Instance()->DeregisterImpl(aId);
194 if (Instance()->Empty()) {
195 // Unset singleton inside mutex lock, but don't call Shutdown until we
196 // unlock, since that involves calling into libusrsctp, which invites
197 // deadlock.
198 maybeTrash = std::move(Instance());
199 }
200 }
201 }
202
203 static RefPtr<DataChannelConnection> Lookup(uintptr_t aId) {
204 StaticMutexAutoLock lock(sInstanceMutex);
205 if (NS_WARN_IF(!Instance())NS_warn_if_impl(!Instance(), "!Instance()", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 205)
) {
206 return nullptr;
207 }
208 return Instance()->LookupImpl(aId);
209 }
210
211 virtual ~DataChannelRegistry() {
212 MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(NS_IsMainThread())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(NS_IsMainThread()))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("NS_IsMainThread()"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 212); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "NS_IsMainThread()"
")"); do { *((volatile int*)__null) = 212; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
213
214 if (NS_WARN_IF(!mConnections.empty())NS_warn_if_impl(!mConnections.empty(), "!mConnections.empty()"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 214)
) {
215 MOZ_DIAGNOSTIC_ASSERT(false)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 215); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "false"
")"); do { *((volatile int*)__null) = 215; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
216 mConnections.clear();
217 }
218
219 MOZ_DIAGNOSTIC_ASSERT(!Instance())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!Instance())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!Instance()))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("!Instance()", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 219); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "!Instance()"
")"); do { *((volatile int*)__null) = 219; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
220 DeinitUsrSctp();
221 }
222
223 private:
224 // This is a singleton class, so don't let just anyone create one of these
225 DataChannelRegistry() {
226 MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(NS_IsMainThread())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(NS_IsMainThread()))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("NS_IsMainThread()"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 226); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "NS_IsMainThread()"
")"); do { *((volatile int*)__null) = 226; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
227 mShutdownBlocker = media::ShutdownBlockingTicket::Create(
228 u"DataChannelRegistry::mShutdownBlocker"_ns,
229 NS_LITERAL_STRING_FROM_CSTRING(__FILE__)static_cast<const nsLiteralString&>( nsLiteralString
(u"" "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
))
, __LINE__229);
230 MOZ_DIAGNOSTIC_ASSERT(!Instance())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!Instance())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!Instance()))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("!Instance()", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 230); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "!Instance()"
")"); do { *((volatile int*)__null) = 230; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
231 InitUsrSctp();
232 }
233
234 static std::unique_ptr<DataChannelRegistry>& Instance() {
235 static std::unique_ptr<DataChannelRegistry> sRegistry;
236 return sRegistry;
237 }
238
239 static std::unique_ptr<DataChannelRegistry>& EnsureInstance() {
240 MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(NS_IsMainThread())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(NS_IsMainThread()))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("NS_IsMainThread()"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 240); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "NS_IsMainThread()"
")"); do { *((volatile int*)__null) = 240; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
241 if (!Instance()) {
242 Instance().reset(new DataChannelRegistry());
243 }
244 return Instance();
245 }
246
247 uintptr_t RegisterImpl(DataChannelConnection* aConnection) {
248 MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(NS_IsMainThread())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(NS_IsMainThread()))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("NS_IsMainThread()"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 248); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "NS_IsMainThread()"
")"); do { *((volatile int*)__null) = 248; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
249 mConnections.emplace(mNextId, aConnection);
250 return mNextId++;
251 }
252
253 void DeregisterImpl(uintptr_t aId) {
254 MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(NS_IsMainThread())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(NS_IsMainThread()))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("NS_IsMainThread()"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 254); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "NS_IsMainThread()"
")"); do { *((volatile int*)__null) = 254; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
255 size_t removed = mConnections.erase(aId);
256 mozilla::Unused << removed;
257 MOZ_DIAGNOSTIC_ASSERT(removed)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(removed)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(removed))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("removed", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 257); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "removed"
")"); do { *((volatile int*)__null) = 257; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
258 }
259
260 bool Empty() const { return mConnections.empty(); }
261
262 RefPtr<DataChannelConnection> LookupImpl(uintptr_t aId) {
263 auto it = mConnections.find(aId);
264 if (NS_WARN_IF(it == mConnections.end())NS_warn_if_impl(it == mConnections.end(), "it == mConnections.end()"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 264)
) {
265 DC_DEBUG(("Can't find connection ulp %p", (void*)aId))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Can't find connection ulp %p"
, (void*)aId); } } while (0)
;
266 return nullptr;
267 }
268 return it->second;
269 }
270
271 static int SctpDtlsOutput(void* addr, void* buffer, size_t length,
272 uint8_t tos, uint8_t set_df) {
273 uintptr_t id = reinterpret_cast<uintptr_t>(addr);
274 RefPtr<DataChannelConnection> connection = DataChannelRegistry::Lookup(id);
275 if (NS_WARN_IF(!connection)NS_warn_if_impl(!connection, "!connection", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 275)
|| connection->InShutdown()) {
276 return 0;
277 }
278 return connection->SctpDtlsOutput(addr, buffer, length, tos, set_df);
279 }
280
281 void InitUsrSctp() {
282 MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(NS_IsMainThread())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(NS_IsMainThread()))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("NS_IsMainThread()"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 282); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "NS_IsMainThread()"
")"); do { *((volatile int*)__null) = 282; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
283#ifndef MOZ_PEERCONNECTION1
284 MOZ_CRASH("Trying to use SCTP/DTLS without dom/media/webrtc/transport")do { do { } while (false); MOZ_ReportCrash("" "Trying to use SCTP/DTLS without dom/media/webrtc/transport"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 284); AnnotateMozCrashReason("MOZ_CRASH(" "Trying to use SCTP/DTLS without dom/media/webrtc/transport"
")"); do { *((volatile int*)__null) = 284; __attribute__((nomerge
)) ::abort(); } while (false); } while (false)
;
285#endif
286
287 DC_DEBUG(("Calling usrsctp_init %p", this))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Calling usrsctp_init %p"
, this); } } while (0)
;
288
289 MOZ_DIAGNOSTIC_ASSERT(!sInitted)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!sInitted)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!sInitted))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("!sInitted", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 289); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "!sInitted"
")"); do { *((volatile int*)__null) = 289; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
290 usrsctp_init(0, DataChannelRegistry::SctpDtlsOutput, debug_printf);
291 sInitted = true;
292
293 // Set logging to SCTP:LogLevel::Debug to get SCTP debugs
294 if (MOZ_LOG_TEST(gSCTPLog, LogLevel::Debug)(__builtin_expect(!!(mozilla::detail::log_test(gSCTPLog, LogLevel
::Debug)), 0))
) {
295 usrsctp_sysctl_set_sctp_debug_on(SCTP_DEBUG_ALL0xffffffff);
296 }
297
298 // Do not send ABORTs in response to INITs (1).
299 // Do not send ABORTs for received Out of the Blue packets (2).
300 usrsctp_sysctl_set_sctp_blackhole(2);
301
302 // Disable the Explicit Congestion Notification extension (currently not
303 // supported by the Firefox code)
304 usrsctp_sysctl_set_sctp_ecn_enable(0);
305
306 // Enable interleaving messages for different streams (incoming)
307 // See: https://tools.ietf.org/html/rfc6458#section-8.1.20
308 usrsctp_sysctl_set_sctp_default_frag_interleave(2);
309
310 // Disabling authentication and dynamic address reconfiguration as neither
311 // of them are used for data channel and only result in additional code
312 // paths being used.
313 usrsctp_sysctl_set_sctp_asconf_enable(0);
314 usrsctp_sysctl_set_sctp_auth_enable(0);
315 }
316
317 void DeinitUsrSctp() {
318 MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(NS_IsMainThread())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(NS_IsMainThread()))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("NS_IsMainThread()"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 318); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "NS_IsMainThread()"
")"); do { *((volatile int*)__null) = 318; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
319 MOZ_DIAGNOSTIC_ASSERT(sInitted)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(sInitted)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(sInitted))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("sInitted", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 319); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "sInitted"
")"); do { *((volatile int*)__null) = 319; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
320 DC_DEBUG(("Calling usrsctp_finish %p", this))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Calling usrsctp_finish %p"
, this); } } while (0)
;
321 usrsctp_finish();
322 sInitted = false;
323 }
324
325 uintptr_t mNextId = 1;
326 std::map<uintptr_t, RefPtr<DataChannelConnection>> mConnections;
327 UniquePtr<media::ShutdownBlockingTicket> mShutdownBlocker;
328 static StaticMutex sInstanceMutex MOZ_UNANNOTATED;
329 static bool sInitted;
330};
331
332bool DataChannelRegistry::sInitted = false;
333
334StaticMutex DataChannelRegistry::sInstanceMutex;
335
336OutgoingMsg::OutgoingMsg(struct sctp_sendv_spa& info, Span<const uint8_t> data)
337 : mData(data), mInfo(&info) {}
338
339void OutgoingMsg::Advance(size_t offset) {
340 mPos += offset;
341 if (mPos > mData.Length()) {
342 mPos = mData.Length();
343 }
344}
345
346/* static */
347UniquePtr<BufferedOutgoingMsg> BufferedOutgoingMsg::CopyFrom(
348 const OutgoingMsg& msg) {
349 nsTArray<uint8_t> data(msg.GetRemainingData());
350 auto info = MakeUnique<struct sctp_sendv_spa>(msg.GetInfo());
351 return WrapUnique(new BufferedOutgoingMsg(std::move(data), std::move(info)));
352}
353
354BufferedOutgoingMsg::BufferedOutgoingMsg(
355 nsTArray<uint8_t>&& data, UniquePtr<struct sctp_sendv_spa>&& info)
356 : OutgoingMsg(*info, data),
357 mDataStorage(std::move(data)),
358 mInfoStorage(std::move(info)) {}
359
360static int receive_cb(struct socket* sock, union sctp_sockstore addr,
361 void* data, size_t datalen, struct sctp_rcvinfo rcv,
362 int flags, void* ulp_info) {
363 DC_DEBUG(("In receive_cb, ulp_info=%p", ulp_info))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "In receive_cb, ulp_info=%p"
, ulp_info); } } while (0)
;
364 uintptr_t id = reinterpret_cast<uintptr_t>(ulp_info);
365 RefPtr<DataChannelConnection> connection = DataChannelRegistry::Lookup(id);
366 if (!connection) {
367 // Unfortunately, we can get callbacks after calling
368 // usrsctp_close(socket), so we need to simply ignore them if we've
369 // already killed the DataChannelConnection object
370 DC_DEBUG((do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignoring receive callback for terminated Connection ulp=%p, %zu bytes"
, ulp_info, datalen); } } while (0)
371 "Ignoring receive callback for terminated Connection ulp=%p, %zu bytes",do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignoring receive callback for terminated Connection ulp=%p, %zu bytes"
, ulp_info, datalen); } } while (0)
372 ulp_info, datalen))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Ignoring receive callback for terminated Connection ulp=%p, %zu bytes"
, ulp_info, datalen); } } while (0)
;
373 return 0;
374 }
375 return connection->ReceiveCallback(sock, data, datalen, rcv, flags);
376}
377
378static RefPtr<DataChannelConnection> GetConnectionFromSocket(
379 struct socket* sock) {
380 struct sockaddr* addrs = nullptr;
381 int naddrs = usrsctp_getladdrs(sock, 0, &addrs);
382 if (naddrs <= 0 || addrs[0].sa_family != AF_CONN123) {
383 return nullptr;
384 }
385 // usrsctp_getladdrs() returns the addresses bound to this socket, which
386 // contains the SctpDataMediaChannel* as sconn_addr. Read the pointer,
387 // then free the list of addresses once we have the pointer. We only open
388 // AF_CONN sockets, and they should all have the sconn_addr set to the
389 // pointer that created them, so [0] is as good as any other.
390 struct sockaddr_conn* sconn =
391 reinterpret_cast<struct sockaddr_conn*>(&addrs[0]);
392 uintptr_t id = reinterpret_cast<uintptr_t>(sconn->sconn_addr);
393 RefPtr<DataChannelConnection> connection = DataChannelRegistry::Lookup(id);
394 usrsctp_freeladdrs(addrs);
395
396 return connection;
397}
398
399// Called when the buffer empties to the threshold value. This is called
400// from SctpDtlsInput() through the sctp stack. SctpDtlsInput() calls
401// usrsctp_conninput() under lock
402int DataChannelConnection::OnThresholdEvent(struct socket* sock,
403 uint32_t sb_free, void* ulp_info) {
404 RefPtr<DataChannelConnection> connection = GetConnectionFromSocket(sock);
405 connection->mLock.AssertCurrentThreadOwns();
406 if (connection) {
407 connection->SendDeferredMessages();
408 } else {
409 DC_ERROR(("Can't find connection for socket %p", sock))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "Can't find connection for socket %p"
, sock); } } while (0)
;
410 }
411 return 0;
412}
413
414DataChannelConnection::~DataChannelConnection() {
415 DC_DEBUG(("Deleting DataChannelConnection %p", (void*)this))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Deleting DataChannelConnection %p"
, (void*)this); } } while (0)
;
416 // This may die on the MainThread, or on the STS thread, or on an
417 // sctp thread if we were in a callback when the DOM side shut things down.
418 ASSERT_WEBRTC(mState == DataChannelConnectionState::Closed)do { static_assert( mozilla::detail::AssertionConditionType<
decltype((mState == DataChannelConnectionState::Closed))>::
isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!((mState == DataChannelConnectionState::Closed)))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("(mState == DataChannelConnectionState::Closed)"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 418); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(mState == DataChannelConnectionState::Closed)"
")"); do { *((volatile int*)__null) = 418; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
419 MOZ_ASSERT(!mMasterSocket)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!mMasterSocket)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!mMasterSocket))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("!mMasterSocket"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 419); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mMasterSocket"
")"); do { *((volatile int*)__null) = 419; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
420 MOZ_ASSERT(mPending.empty())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mPending.empty())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mPending.empty()))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("mPending.empty()"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 420); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mPending.empty()"
")"); do { *((volatile int*)__null) = 420; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
421
422 if (!IsSTSThread()) {
423 // We may be on MainThread *or* on an sctp thread (being called from
424 // receive_cb() or SctpDtlsOutput())
425 if (mInternalIOThread) {
426 // Avoid spinning the event thread from here (which if we're mainthread
427 // is in the event loop already)
428 nsCOMPtr<nsIRunnable> r = WrapRunnable(
429 nsCOMPtr<nsIThread>(mInternalIOThread), &nsIThread::AsyncShutdown);
430 Dispatch(r.forget());
431 }
432 } else {
433 // on STS, safe to call shutdown
434 if (mInternalIOThread) {
435 mInternalIOThread->Shutdown();
436 }
437 }
438}
439
440void DataChannelConnection::Destroy() {
441 // Though it's probably ok to do this and close the sockets;
442 // if we really want it to do true clean shutdowns it can
443 // create a dependant Internal object that would remain around
444 // until the network shut down the association or timed out.
445 DC_DEBUG(("Destroying DataChannelConnection %p", (void*)this))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Destroying DataChannelConnection %p"
, (void*)this); } } while (0)
;
446 ASSERT_WEBRTC(NS_IsMainThread())do { static_assert( mozilla::detail::AssertionConditionType<
decltype((NS_IsMainThread()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((NS_IsMainThread())))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("(NS_IsMainThread())"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 446); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(NS_IsMainThread())"
")"); do { *((volatile int*)__null) = 446; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
447 CloseAll();
448
449 MutexAutoLock lock(mLock);
450 // If we had a pending reset, we aren't waiting for it - clear the list so
451 // we can deregister this DataChannelConnection without leaking.
452 ClearResets();
453
454#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED1
455 MOZ_DIAGNOSTIC_ASSERT(mSTS)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mSTS)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(mSTS))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("mSTS", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 455); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "mSTS"
")"); do { *((volatile int*)__null) = 455; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
456 auto self = DataChannelRegistry::Lookup(mId);
457 MOZ_DIAGNOSTIC_ASSERT(self)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(self)>::isValid, "invalid assertion condition"); if
((__builtin_expect(!!(!(!!(self))), 0))) { do { } while (false
); MOZ_ReportAssertionFailure("self", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 457); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "self"
")"); do { *((volatile int*)__null) = 457; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
458 MOZ_DIAGNOSTIC_ASSERT(this == self.get())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(this == self.get())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(this == self.get()))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("this == self.get()"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 458); AnnotateMozCrashReason("MOZ_DIAGNOSTIC_ASSERT" "(" "this == self.get()"
")"); do { *((volatile int*)__null) = 458; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
459#endif
460 mListener = nullptr;
461 // Finish Destroy on STS thread to avoid bug 876167 - once that's fixed,
462 // the usrsctp_close() calls can move back here (and just proxy the
463 // disconnect_all())
464 RUN_ON_THREAD(mSTS,
465 WrapRunnable(RefPtr<DataChannelConnection>(this),
466 &DataChannelConnection::DestroyOnSTS, mSocket,
467 mMasterSocket),
468 NS_DISPATCH_NORMALnsIEventTarget::DISPATCH_NORMAL);
469
470 // These will be released on STS
471 mSocket = nullptr;
472 mMasterSocket = nullptr; // also a flag that we've Destroyed this connection
473
474 // We can't get any more *new* callbacks from the SCTP library
475
476 // All existing callbacks have refs to DataChannelConnection - however,
477 // we need to handle their destroying the object off mainthread/STS
478
479 // nsDOMDataChannel objects have refs to DataChannels that have refs to us
480}
481
482void DataChannelConnection::DestroyOnSTS(struct socket* aMasterSocket,
483 struct socket* aSocket) {
484 if (aSocket && aSocket != aMasterSocket) usrsctp_close(aSocket);
485 if (aMasterSocket) usrsctp_close(aMasterSocket);
486
487 usrsctp_deregister_address(reinterpret_cast<void*>(mId));
488 DC_DEBUG(do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Deregistered %p from the SCTP stack."
, reinterpret_cast<void*>(mId)); } } while (0)
489 ("Deregistered %p from the SCTP stack.", reinterpret_cast<void*>(mId)))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Deregistered %p from the SCTP stack."
, reinterpret_cast<void*>(mId)); } } while (0)
;
490#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED1
491 mShutdown = true;
492 DC_DEBUG(("Shutting down connection %p, id %p", this, (void*)mId))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Shutting down connection %p, id %p"
, this, (void*)mId); } } while (0)
;
493#endif
494
495 disconnect_all();
496 mTransportHandler = nullptr;
497 GetMainThreadSerialEventTarget()->Dispatch(NS_NewRunnableFunction(
498 "DataChannelConnection::Destroy",
499 [id = mId]() { DataChannelRegistry::Deregister(id); }));
500}
501
502Maybe<RefPtr<DataChannelConnection>> DataChannelConnection::Create(
503 DataChannelConnection::DataConnectionListener* aListener,
504 nsISerialEventTarget* aTarget, MediaTransportHandler* aHandler,
505 const uint16_t aLocalPort, const uint16_t aNumStreams,
506 const Maybe<uint64_t>& aMaxMessageSize) {
507 ASSERT_WEBRTC(NS_IsMainThread())do { static_assert( mozilla::detail::AssertionConditionType<
decltype((NS_IsMainThread()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((NS_IsMainThread())))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("(NS_IsMainThread())"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 507); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(NS_IsMainThread())"
")"); do { *((volatile int*)__null) = 507; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
508
509 RefPtr<DataChannelConnection> connection = new DataChannelConnection(
510 aListener, aTarget, aHandler); // Walks into a bar
511 return connection->Init(aLocalPort, aNumStreams, aMaxMessageSize)
512 ? Some(connection)
513 : Nothing();
514}
515
516DataChannelConnection::DataChannelConnection(
517 DataChannelConnection::DataConnectionListener* aListener,
518 nsISerialEventTarget* aTarget, MediaTransportHandler* aHandler)
519 : NeckoTargetHolder(aTarget),
520 mLock("netwerk::sctp::DataChannelConnection"),
521 mListener(aListener),
522 mTransportHandler(aHandler) {
523 DC_VERBOSE(("Constructor DataChannelConnection=%p, listener=%p", this,do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Verbose)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Verbose, "Constructor DataChannelConnection=%p, listener=%p"
, this, mListener.get()); } } while (0)
524 mListener.get()))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Verbose)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Verbose, "Constructor DataChannelConnection=%p, listener=%p"
, this, mListener.get()); } } while (0)
;
525#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED1
526 mShutdown = false;
527#endif
528}
529
530bool DataChannelConnection::Init(const uint16_t aLocalPort,
531 const uint16_t aNumStreams,
532 const Maybe<uint64_t>& aMaxMessageSize) {
533 ASSERT_WEBRTC(NS_IsMainThread())do { static_assert( mozilla::detail::AssertionConditionType<
decltype((NS_IsMainThread()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((NS_IsMainThread())))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("(NS_IsMainThread())"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 533); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(NS_IsMainThread())"
")"); do { *((volatile int*)__null) = 533; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
534
535 struct sctp_initmsg initmsg = {};
536 struct sctp_assoc_value av = {};
537 struct sctp_event event = {};
538 socklen_t len;
539
540 uint16_t event_types[] = {
541 SCTP_ASSOC_CHANGE0x0001, SCTP_PEER_ADDR_CHANGE0x0002,
542 SCTP_REMOTE_ERROR0x0003, SCTP_SHUTDOWN_EVENT0x0005,
543 SCTP_ADAPTATION_INDICATION0x0006, SCTP_PARTIAL_DELIVERY_EVENT0x0007,
544 SCTP_SEND_FAILED_EVENT0x000e, SCTP_STREAM_RESET_EVENT0x0009,
545 SCTP_STREAM_CHANGE_EVENT0x000d};
546 {
547 // MutexAutoLock lock(mLock); Not needed since we're on mainthread always
548 mLocalPort = aLocalPort;
549 SetMaxMessageSize(aMaxMessageSize.isSome(), aMaxMessageSize.valueOr(0));
550 }
551
552 mId = DataChannelRegistry::Register(this);
553
554 // XXX FIX! make this a global we get once
555 // Find the STS thread
556 nsresult rv;
557 mSTS = mozilla::components::SocketTransport::Service(&rv);
558 MOZ_ASSERT(NS_SUCCEEDED(rv))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(((bool)(__builtin_expect(!!(!NS_FAILED_impl(rv)), 1)
)))>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(((bool)(__builtin_expect(!!(!NS_FAILED_impl(rv)), 1)
))))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("((bool)(__builtin_expect(!!(!NS_FAILED_impl(rv)), 1)))", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 558); AnnotateMozCrashReason("MOZ_ASSERT" "(" "((bool)(__builtin_expect(!!(!NS_FAILED_impl(rv)), 1)))"
")"); do { *((volatile int*)__null) = 558; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
559
560 socklen_t buf_size = 1024 * 1024;
561
562 // Open sctp with a callback
563 if ((mMasterSocket =
564 usrsctp_socket(AF_CONN123, SOCK_STREAMSOCK_STREAM, IPPROTO_SCTPIPPROTO_SCTP, receive_cb,
565 &DataChannelConnection::OnThresholdEvent,
566 usrsctp_sysctl_get_sctp_sendspace() / 2,
567 reinterpret_cast<void*>(mId))) == nullptr) {
568 goto error_cleanup;
569 }
570
571 if (usrsctp_setsockopt(mMasterSocket, SOL_SOCKET1, SO_RCVBUF8,
572 (const void*)&buf_size, sizeof(buf_size)) < 0) {
573 DC_ERROR(("Couldn't change receive buffer size on SCTP socket"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "Couldn't change receive buffer size on SCTP socket"
); } } while (0)
;
574 goto error_cleanup;
575 }
576 if (usrsctp_setsockopt(mMasterSocket, SOL_SOCKET1, SO_SNDBUF7,
577 (const void*)&buf_size, sizeof(buf_size)) < 0) {
578 DC_ERROR(("Couldn't change send buffer size on SCTP socket"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "Couldn't change send buffer size on SCTP socket"
); } } while (0)
;
579 goto error_cleanup;
580 }
581
582 // Make non-blocking for bind/connect. SCTP over UDP defaults to non-blocking
583 // in associations for normal IO
584 if (usrsctp_set_non_blocking(mMasterSocket, 1) < 0) {
585 DC_ERROR(("Couldn't set non_blocking on SCTP socket"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "Couldn't set non_blocking on SCTP socket"
); } } while (0)
;
586 // We can't handle connect() safely if it will block, not that this will
587 // even happen.
588 goto error_cleanup;
589 }
590
591 // Make sure when we close the socket, make sure it doesn't call us back
592 // again! This would cause it try to use an invalid DataChannelConnection
593 // pointer
594 struct linger l;
595 l.l_onoff = 1;
596 l.l_linger = 0;
597 if (usrsctp_setsockopt(mMasterSocket, SOL_SOCKET1, SO_LINGER13, (const void*)&l,
598 (socklen_t)sizeof(struct linger)) < 0) {
599 DC_ERROR(("Couldn't set SO_LINGER on SCTP socket"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "Couldn't set SO_LINGER on SCTP socket"
); } } while (0)
;
600 // unsafe to allow it to continue if this fails
601 goto error_cleanup;
602 }
603
604 // XXX Consider disabling this when we add proper SDP negotiation.
605 // We may want to leave enabled for supporting 'cloning' of SDP offers, which
606 // implies re-use of the same pseudo-port number, or forcing a renegotiation.
607 {
608 const int option_value = 1;
609 if (usrsctp_setsockopt(mMasterSocket, IPPROTO_SCTPIPPROTO_SCTP, SCTP_REUSE_PORT0x0000001c,
610 (const void*)&option_value,
611 (socklen_t)sizeof(option_value)) < 0) {
612 DC_WARN(("Couldn't set SCTP_REUSE_PORT on SCTP socket"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Warning)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Warning, "Couldn't set SCTP_REUSE_PORT on SCTP socket"
); } } while (0)
;
613 }
614 if (usrsctp_setsockopt(mMasterSocket, IPPROTO_SCTPIPPROTO_SCTP, SCTP_NODELAY0x00000004,
615 (const void*)&option_value,
616 (socklen_t)sizeof(option_value)) < 0) {
617 DC_WARN(("Couldn't set SCTP_NODELAY on SCTP socket"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Warning)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Warning, "Couldn't set SCTP_NODELAY on SCTP socket"
); } } while (0)
;
618 }
619 }
620
621 // Set explicit EOR
622 {
623 const int option_value = 1;
624 if (usrsctp_setsockopt(mMasterSocket, IPPROTO_SCTPIPPROTO_SCTP, SCTP_EXPLICIT_EOR0x0000001b,
625 (const void*)&option_value,
626 (socklen_t)sizeof(option_value)) < 0) {
627 DC_ERROR(("*** failed to enable explicit EOR mode %d", errno))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "*** failed to enable explicit EOR mode %d"
, (*__errno_location ())); } } while (0)
;
628 goto error_cleanup;
629 }
630 }
631
632 // Enable ndata
633 // TODO: Bug 1381145, enable this once ndata has been deployed
634#if 0
635 av.assoc_id = SCTP_FUTURE_ASSOC0;
636 av.assoc_value = 1;
637 if (usrsctp_setsockopt(mMasterSocket, IPPROTO_SCTPIPPROTO_SCTP, SCTP_INTERLEAVING_SUPPORTED, &av,
638 (socklen_t)sizeof(struct sctp_assoc_value)) < 0) {
639 DC_ERROR(("*** failed enable ndata errno %d", errno))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "*** failed enable ndata errno %d"
, (*__errno_location ())); } } while (0)
;
640 goto error_cleanup;
641 }
642#endif
643
644 av.assoc_id = SCTP_ALL_ASSOC2;
645 av.assoc_value = SCTP_ENABLE_RESET_STREAM_REQ0x00000001 | SCTP_ENABLE_CHANGE_ASSOC_REQ0x00000004;
646 if (usrsctp_setsockopt(mMasterSocket, IPPROTO_SCTPIPPROTO_SCTP, SCTP_ENABLE_STREAM_RESET0x00000900,
647 &av, (socklen_t)sizeof(struct sctp_assoc_value)) < 0) {
648 DC_ERROR(("*** failed enable stream reset errno %d", errno))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "*** failed enable stream reset errno %d"
, (*__errno_location ())); } } while (0)
;
649 goto error_cleanup;
650 }
651
652 /* Enable the events of interest. */
653 event.se_assoc_id = SCTP_ALL_ASSOC2;
654 event.se_on = 1;
655 for (unsigned short event_type : event_types) {
656 event.se_type = event_type;
657 if (usrsctp_setsockopt(mMasterSocket, IPPROTO_SCTPIPPROTO_SCTP, SCTP_EVENT0x0000001e, &event,
658 sizeof(event)) < 0) {
659 DC_ERROR(("*** failed setsockopt SCTP_EVENT errno %d", errno))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "*** failed setsockopt SCTP_EVENT errno %d"
, (*__errno_location ())); } } while (0)
;
660 goto error_cleanup;
661 }
662 }
663
664 len = sizeof(initmsg);
665 if (usrsctp_getsockopt(mMasterSocket, IPPROTO_SCTPIPPROTO_SCTP, SCTP_INITMSG0x00000003, &initmsg,
666 &len) < 0) {
667 DC_ERROR(("*** failed getsockopt SCTP_INITMSG"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "*** failed getsockopt SCTP_INITMSG"
); } } while (0)
;
668 goto error_cleanup;
669 }
670 DC_DEBUG(("Setting number of SCTP streams to %u, was %u/%u", aNumStreams,do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Setting number of SCTP streams to %u, was %u/%u"
, aNumStreams, initmsg.sinit_num_ostreams, initmsg.sinit_max_instreams
); } } while (0)
671 initmsg.sinit_num_ostreams, initmsg.sinit_max_instreams))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Setting number of SCTP streams to %u, was %u/%u"
, aNumStreams, initmsg.sinit_num_ostreams, initmsg.sinit_max_instreams
); } } while (0)
;
672 initmsg.sinit_num_ostreams = aNumStreams;
673 initmsg.sinit_max_instreams = MAX_NUM_STREAMS(2048);
674 if (usrsctp_setsockopt(mMasterSocket, IPPROTO_SCTPIPPROTO_SCTP, SCTP_INITMSG0x00000003, &initmsg,
675 (socklen_t)sizeof(initmsg)) < 0) {
676 DC_ERROR(("*** failed setsockopt SCTP_INITMSG, errno %d", errno))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "*** failed setsockopt SCTP_INITMSG, errno %d"
, (*__errno_location ())); } } while (0)
;
677 goto error_cleanup;
678 }
679
680 mSocket = nullptr;
681 mSTS->Dispatch(
682 NS_NewRunnableFunction("DataChannelConnection::Init", [id = mId]() {
683 usrsctp_register_address(reinterpret_cast<void*>(id));
684 DC_DEBUG(("Registered %p within the SCTP stack.",do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Registered %p within the SCTP stack."
, reinterpret_cast<void*>(id)); } } while (0)
685 reinterpret_cast<void*>(id)))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Registered %p within the SCTP stack."
, reinterpret_cast<void*>(id)); } } while (0)
;
686 }));
687
688 return true;
689
690error_cleanup:
691 DataChannelRegistry::Deregister(mId);
692 usrsctp_close(mMasterSocket);
693 mMasterSocket = nullptr;
694 return false;
695}
696
697// Only called on MainThread, mMaxMessageSize is read on other threads
698void DataChannelConnection::SetMaxMessageSize(bool aMaxMessageSizeSet,
699 uint64_t aMaxMessageSize) {
700 ASSERT_WEBRTC(NS_IsMainThread())do { static_assert( mozilla::detail::AssertionConditionType<
decltype((NS_IsMainThread()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((NS_IsMainThread())))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("(NS_IsMainThread())"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 700); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(NS_IsMainThread())"
")"); do { *((volatile int*)__null) = 700; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
701 MutexAutoLock lock(mLock);
702
703 if (mMaxMessageSizeSet && !aMaxMessageSizeSet) {
704 // Don't overwrite already set MMS with default values
705 return;
706 }
707
708 mMaxMessageSizeSet = aMaxMessageSizeSet;
709 mMaxMessageSize = aMaxMessageSize;
710
711 nsresult rv;
712 nsCOMPtr<nsIPrefService> prefs;
713 prefs = mozilla::components::Preferences::Service(&rv);
714 if (!NS_WARN_IF(NS_FAILED(rv))NS_warn_if_impl(((bool)(__builtin_expect(!!(NS_FAILED_impl(rv
)), 0))), "NS_FAILED(rv)", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 714)
) {
715 nsCOMPtr<nsIPrefBranch> branch = do_QueryInterface(prefs);
716
717 if (branch) {
718 int32_t temp;
719 if (!NS_FAILED(branch->GetIntPref(((bool)(__builtin_expect(!!(NS_FAILED_impl(branch->GetIntPref
( "media.peerconnection.sctp.force_maximum_message_size", &
temp))), 0)))
720 "media.peerconnection.sctp.force_maximum_message_size", &temp))((bool)(__builtin_expect(!!(NS_FAILED_impl(branch->GetIntPref
( "media.peerconnection.sctp.force_maximum_message_size", &
temp))), 0)))
) {
721 if (temp >= 0) {
722 mMaxMessageSize = (uint64_t)temp;
723 }
724 }
725 }
726 }
727
728 // Fix remote MMS. This code exists, so future implementations of
729 // RTCSctpTransport.maxMessageSize can simply provide that value from
730 // GetMaxMessageSize.
731
732 // TODO: Bug 1382779, once resolved, can be increased to
733 // min(Uint8ArrayMaxSize, UINT32_MAX)
734 // TODO: Bug 1381146, once resolved, can be increased to whatever we support
735 // then (hopefully
736 // SIZE_MAX)
737 if (mMaxMessageSize == 0 ||
738 mMaxMessageSize > WEBRTC_DATACHANNEL_MAX_MESSAGE_SIZE_REMOTE2147483637) {
739 mMaxMessageSize = WEBRTC_DATACHANNEL_MAX_MESSAGE_SIZE_REMOTE2147483637;
740 }
741
742 DC_DEBUG(("Maximum message size (outgoing data): %" PRIu64do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Maximum message size (outgoing data): %"
"l" "u" " (set=%s, enforced=%s)", mMaxMessageSize, mMaxMessageSizeSet
? "yes" : "no", aMaxMessageSize != mMaxMessageSize ? "yes" :
"no"); } } while (0)
743 " (set=%s, enforced=%s)",do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Maximum message size (outgoing data): %"
"l" "u" " (set=%s, enforced=%s)", mMaxMessageSize, mMaxMessageSizeSet
? "yes" : "no", aMaxMessageSize != mMaxMessageSize ? "yes" :
"no"); } } while (0)
744 mMaxMessageSize, mMaxMessageSizeSet ? "yes" : "no",do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Maximum message size (outgoing data): %"
"l" "u" " (set=%s, enforced=%s)", mMaxMessageSize, mMaxMessageSizeSet
? "yes" : "no", aMaxMessageSize != mMaxMessageSize ? "yes" :
"no"); } } while (0)
745 aMaxMessageSize != mMaxMessageSize ? "yes" : "no"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Maximum message size (outgoing data): %"
"l" "u" " (set=%s, enforced=%s)", mMaxMessageSize, mMaxMessageSizeSet
? "yes" : "no", aMaxMessageSize != mMaxMessageSize ? "yes" :
"no"); } } while (0)
;
746}
747
748uint64_t DataChannelConnection::GetMaxMessageSize() {
749 MutexAutoLock lock(mLock);
750 return mMaxMessageSize;
751}
752
753void DataChannelConnection::AppendStatsToReport(
754 const UniquePtr<dom::RTCStatsCollection>& aReport,
755 const DOMHighResTimeStamp aTimestamp) const {
756 ASSERT_WEBRTC(NS_IsMainThread())do { static_assert( mozilla::detail::AssertionConditionType<
decltype((NS_IsMainThread()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((NS_IsMainThread())))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("(NS_IsMainThread())"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 756); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(NS_IsMainThread())"
")"); do { *((volatile int*)__null) = 756; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
757 nsString temp;
758 for (const RefPtr<DataChannel>& chan : mChannels.GetAll()) {
759 // If channel is empty, ignore
760 if (!chan) {
761 continue;
762 }
763 mozilla::dom::RTCDataChannelStats stats;
764 nsString id = u"dc"_ns;
765 id.AppendInt(chan->GetStream());
766 stats.mId.Construct(id);
767 chan->GetLabel(temp);
768 stats.mTimestamp.Construct(aTimestamp);
769 stats.mType.Construct(mozilla::dom::RTCStatsType::Data_channel);
770 stats.mLabel.Construct(temp);
771 chan->GetProtocol(temp);
772 stats.mProtocol.Construct(temp);
773 stats.mDataChannelIdentifier.Construct(chan->GetStream());
774 {
775 using State = mozilla::dom::RTCDataChannelState;
776 State state;
777 switch (chan->GetReadyState()) {
778 case DataChannelState::Connecting:
779 state = State::Connecting;
780 break;
781 case DataChannelState::Open:
782 state = State::Open;
783 break;
784 case DataChannelState::Closing:
785 state = State::Closing;
786 break;
787 case DataChannelState::Closed:
788 state = State::Closed;
789 break;
790 };
791 stats.mState.Construct(state);
792 }
793 auto counters = chan->GetTrafficCounters();
794 stats.mMessagesSent.Construct(counters.mMessagesSent);
795 stats.mBytesSent.Construct(counters.mBytesSent);
796 stats.mMessagesReceived.Construct(counters.mMessagesReceived);
797 stats.mBytesReceived.Construct(counters.mBytesReceived);
798 if (!aReport->mDataChannelStats.AppendElement(stats, fallible)) {
799 mozalloc_handle_oom(0);
800 }
801 }
802}
803
804#ifdef MOZ_PEERCONNECTION1
805
806bool DataChannelConnection::ConnectToTransport(const std::string& aTransportId,
807 const bool aClient,
808 const uint16_t aLocalPort,
809 const uint16_t aRemotePort) {
810 MutexAutoLock lock(mLock);
811
812 MOZ_ASSERT(mMasterSocket,do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mMasterSocket)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mMasterSocket))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("mMasterSocket" " ("
"SCTP wasn't initialized before ConnectToTransport!" ")", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 813); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mMasterSocket"
") (" "SCTP wasn't initialized before ConnectToTransport!" ")"
); do { *((volatile int*)__null) = 813; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
813 "SCTP wasn't initialized before ConnectToTransport!")do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mMasterSocket)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mMasterSocket))), 0))) { do {
} while (false); MOZ_ReportAssertionFailure("mMasterSocket" " ("
"SCTP wasn't initialized before ConnectToTransport!" ")", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 813); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mMasterSocket"
") (" "SCTP wasn't initialized before ConnectToTransport!" ")"
); do { *((volatile int*)__null) = 813; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
814 static const auto paramString =
815 [](const std::string& tId, const Maybe<bool>& client,
816 const uint16_t localPort, const uint16_t remotePort) -> std::string {
817 std::ostringstream stream;
818 stream << "Transport ID: '" << tId << "', Role: '"
819 << (client ? (client.value() ? "client" : "server") : "")
820 << "', Local Port: '" << localPort << "', Remote Port: '"
821 << remotePort << "'";
822 return stream.str();
823 };
824
825 const auto params =
826 paramString(aTransportId, Some(aClient), aLocalPort, aRemotePort);
827 DC_DEBUG(("ConnectToTransport connecting DTLS transport with parameters: %s",do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "ConnectToTransport connecting DTLS transport with parameters: %s"
, params.c_str()); } } while (0)
828 params.c_str()))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "ConnectToTransport connecting DTLS transport with parameters: %s"
, params.c_str()); } } while (0)
;
829
830 DataChannelConnectionState state = GetState();
831 if (state == DataChannelConnectionState::Open) {
832 if (aTransportId == mTransportId && mAllocateEven.isSome() &&
833 mAllocateEven.value() == aClient && mLocalPort == aLocalPort &&
834 mRemotePort == aRemotePort) {
835 DC_WARN(do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Warning)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Warning, "Skipping attempt to connect to an already OPEN transport with "
"identical parameters."); } } while (0)
836 ("Skipping attempt to connect to an already OPEN transport with "do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Warning)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Warning, "Skipping attempt to connect to an already OPEN transport with "
"identical parameters."); } } while (0)
837 "identical parameters."))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Warning)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Warning, "Skipping attempt to connect to an already OPEN transport with "
"identical parameters."); } } while (0)
;
838 return true;
839 }
840 DC_WARN(do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Warning)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Warning, "Attempting to connect to an already OPEN transport, because "
"different parameters were provided."); } } while (0)
841 ("Attempting to connect to an already OPEN transport, because "do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Warning)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Warning, "Attempting to connect to an already OPEN transport, because "
"different parameters were provided."); } } while (0)
842 "different parameters were provided."))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Warning)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Warning, "Attempting to connect to an already OPEN transport, because "
"different parameters were provided."); } } while (0)
;
843 DC_WARN(("Original transport parameters: %s",do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Warning)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Warning, "Original transport parameters: %s"
, paramString(mTransportId, mAllocateEven, mLocalPort, aRemotePort
) .c_str()); } } while (0)
844 paramString(mTransportId, mAllocateEven, mLocalPort, aRemotePort)do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Warning)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Warning, "Original transport parameters: %s"
, paramString(mTransportId, mAllocateEven, mLocalPort, aRemotePort
) .c_str()); } } while (0)
845 .c_str()))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Warning)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Warning, "Original transport parameters: %s"
, paramString(mTransportId, mAllocateEven, mLocalPort, aRemotePort
) .c_str()); } } while (0)
;
846 DC_WARN(("New transport parameters: %s", params.c_str()))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Warning)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Warning, "New transport parameters: %s"
, params.c_str()); } } while (0)
;
847 }
848 if (NS_WARN_IF(aTransportId.empty())NS_warn_if_impl(aTransportId.empty(), "aTransportId.empty()",
"/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 848)
) {
849 return false;
850 }
851
852 mLocalPort = aLocalPort;
853 mRemotePort = aRemotePort;
854 SetState(DataChannelConnectionState::Connecting);
855 mAllocateEven = Some(aClient);
856
857 // Could be faster. Probably doesn't matter.
858 while (auto channel = mChannels.Get(INVALID_STREAM(0xFFFF))) {
859 mChannels.Remove(channel);
860 channel->mStream = FindFreeStream();
861 if (channel->mStream != INVALID_STREAM(0xFFFF)) {
862 mChannels.Insert(channel);
863 }
864 }
865 RUN_ON_THREAD(mSTS,
866 WrapRunnable(RefPtr<DataChannelConnection>(this),
867 &DataChannelConnection::SetSignals, aTransportId),
868 NS_DISPATCH_NORMALnsIEventTarget::DISPATCH_NORMAL);
869 return true;
870}
871
872void DataChannelConnection::SetSignals(const std::string& aTransportId) {
873 ASSERT_WEBRTC(IsSTSThread())do { static_assert( mozilla::detail::AssertionConditionType<
decltype((IsSTSThread()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((IsSTSThread())))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("(IsSTSThread())"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 873); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(IsSTSThread())"
")"); do { *((volatile int*)__null) = 873; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
874 {
875 MutexAutoLock lock(mLock);
876 mTransportId = aTransportId;
877 }
878 if (!mConnectedToTransportHandler) {
879 mTransportHandler->SignalPacketReceived.connect(
880 this, &DataChannelConnection::SctpDtlsInput);
881 mTransportHandler->SignalStateChange.connect(
882 this, &DataChannelConnection::TransportStateChange);
883 mConnectedToTransportHandler = true;
884 }
885 // SignalStateChange() doesn't call you with the initial state
886 if (mTransportHandler->GetState(mTransportId, false) ==
887 TransportLayer::TS_OPEN) {
888 DC_DEBUG(("Setting transport signals, dtls already open"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Setting transport signals, dtls already open"
); } } while (0)
;
889 CompleteConnect();
890 } else {
891 DC_DEBUG(("Setting transport signals, dtls not open yet"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Setting transport signals, dtls not open yet"
); } } while (0)
;
892 }
893}
894
895void DataChannelConnection::TransportStateChange(
896 const std::string& aTransportId, TransportLayer::State aState) {
897 ASSERT_WEBRTC(IsSTSThread())do { static_assert( mozilla::detail::AssertionConditionType<
decltype((IsSTSThread()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((IsSTSThread())))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("(IsSTSThread())"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 897); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(IsSTSThread())"
")"); do { *((volatile int*)__null) = 897; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
898 if (aTransportId == mTransportId) {
899 if (aState == TransportLayer::TS_OPEN) {
900 DC_DEBUG(("Transport is open!"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Transport is open!"
); } } while (0)
;
901 CompleteConnect();
902 } else if (aState == TransportLayer::TS_CLOSED ||
903 aState == TransportLayer::TS_NONE ||
904 aState == TransportLayer::TS_ERROR) {
905 DC_DEBUG(("Transport is closed!"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Transport is closed!"
); } } while (0)
;
906 Stop();
907 }
908 }
909}
910
911void DataChannelConnection::CompleteConnect() {
912 MutexAutoLock lock(mLock);
913
914 DC_DEBUG(("dtls open"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "dtls open"); } }
while (0)
;
915 ASSERT_WEBRTC(IsSTSThread())do { static_assert( mozilla::detail::AssertionConditionType<
decltype((IsSTSThread()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((IsSTSThread())))), 0))) { do
{ } while (false); MOZ_ReportAssertionFailure("(IsSTSThread())"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 915); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(IsSTSThread())"
")"); do { *((volatile int*)__null) = 915; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
916 if (!mMasterSocket) {
917 return;
918 }
919
920 struct sockaddr_conn addr = {};
921 addr.sconn_family = AF_CONN123;
922# if defined(__Userspace_os_Darwin)
923 addr.sconn_len = sizeof(addr);
924# endif
925 addr.sconn_port = htons(mLocalPort)__bswap_16 (mLocalPort);
926 addr.sconn_addr = reinterpret_cast<void*>(mId);
927
928 DC_DEBUG(("Calling usrsctp_bind"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Calling usrsctp_bind"
); } } while (0)
;
929 int r = usrsctp_bind(mMasterSocket, reinterpret_cast<struct sockaddr*>(&addr),
930 sizeof(addr));
931 if (r < 0) {
932 DC_ERROR(("usrsctp_bind failed: %d", r))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "usrsctp_bind failed: %d"
, r); } } while (0)
;
933 } else {
934 // This is the remote addr
935 addr.sconn_port = htons(mRemotePort)__bswap_16 (mRemotePort);
936 DC_DEBUG(("Calling usrsctp_connect"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Calling usrsctp_connect"
); } } while (0)
;
937 r = usrsctp_connect(
938 mMasterSocket, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr));
939 if (r >= 0 || errno(*__errno_location ()) == EINPROGRESS115) {
940 struct sctp_paddrparams paddrparams = {};
941 socklen_t opt_len;
942
943 memcpy(&paddrparams.spp_address, &addr, sizeof(struct sockaddr_conn));
944 opt_len = (socklen_t)sizeof(struct sctp_paddrparams);
945 r = usrsctp_getsockopt(mMasterSocket, IPPROTO_SCTPIPPROTO_SCTP, SCTP_PEER_ADDR_PARAMS0x0000000a,
946 &paddrparams, &opt_len);
947 if (r < 0) {
948 DC_ERROR(("usrsctp_getsockopt failed: %d", r))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "usrsctp_getsockopt failed: %d"
, r); } } while (0)
;
949 } else {
950 // This field is misnamed. |spp_pathmtu| represents the maximum
951 // _payload_ size in libusrsctp. So:
952 // 1280 (a reasonable IPV6 MTU according to RFC 8831)
953 // -12 (sctp header)
954 // -24 (GCM sipher)
955 // -13 (DTLS record header)
956 // -8 (UDP header)
957 // -4 (TURN ChannelData)
958 // -40 (IPV6 header)
959 // = 1179
960 // We could further restrict this, because RFC 8831 suggests a starting
961 // IPV4 path MTU of 1200, which would lead to a value of 1115.
962 // I suspect that in practice the path MTU for IPV4 is substantially
963 // larger than 1200.
964 paddrparams.spp_pathmtu = 1179;
965 paddrparams.spp_flags &= ~SPP_PMTUD_ENABLE0x00000008;
966 paddrparams.spp_flags |= SPP_PMTUD_DISABLE0x00000010;
967 opt_len = (socklen_t)sizeof(struct sctp_paddrparams);
968 r = usrsctp_setsockopt(mMasterSocket, IPPROTO_SCTPIPPROTO_SCTP,
969 SCTP_PEER_ADDR_PARAMS0x0000000a, &paddrparams, opt_len);
970 if (r < 0) {
971 DC_ERROR(("usrsctp_getsockopt failed: %d", r))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "usrsctp_getsockopt failed: %d"
, r); } } while (0)
;
972 } else {
973 DC_ERROR(("usrsctp: PMTUD disabled, MTU set to %u",do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "usrsctp: PMTUD disabled, MTU set to %u"
, paddrparams.spp_pathmtu); } } while (0)
974 paddrparams.spp_pathmtu))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "usrsctp: PMTUD disabled, MTU set to %u"
, paddrparams.spp_pathmtu); } } while (0)
;
975 }
976 }
977 }
978 if (r < 0) {
979 if (errno(*__errno_location ()) == EINPROGRESS115) {
980 // non-blocking
981 return;
982 }
983 DC_ERROR(("usrsctp_connect failed: %d", errno))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "usrsctp_connect failed: %d"
, (*__errno_location ())); } } while (0)
;
984 SetState(DataChannelConnectionState::Closed);
985 } else {
986 // We fire ON_CONNECTION via SCTP_COMM_UP when we get that
987 return;
988 }
989 }
990 // Note: currently this doesn't actually notify the application
991 Dispatch(do_AddRef(new DataChannelOnMessageAvailable(
992 DataChannelOnMessageAvailable::EventType::OnConnection, this)));
993}
994
995// Process any pending Opens
996void DataChannelConnection::ProcessQueuedOpens() {
997 std::set<RefPtr<DataChannel>> temp(std::move(mPending));
17
Object 'mPending' of type 'std::set' is left in a valid but unspecified state after move
998 for (auto channel : temp) {
999 if (channel->mHasFinishedOpen) {
18
Assuming field 'mHasFinishedOpen' is true
19
Taking true branch
1000 DC_DEBUG(("Processing queued open for %p (%u)", channel.get(),do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Processing queued open for %p (%u)"
, channel.get(), channel->mStream); } } while (0)
20
Assuming the condition is true
21
Taking false branch
22
Loop condition is false. Exiting loop
1001 channel->mStream))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Processing queued open for %p (%u)"
, channel.get(), channel->mStream); } } while (0)
;
1002 channel->mHasFinishedOpen = false;
1003 // OpenFinish returns a reference itself, so we need to take it can
1004 // Release it
1005 channel = OpenFinish(channel.forget()); // may reset the flag and re-push
23
Calling 'DataChannelConnection::OpenFinish'
1006 } else {
1007 NS_ASSERTION(false,do { if (!(false)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "How did a DataChannel get queued without the "
"mHasFinishedOpen flag?", "false", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 1009); MOZ_PretendNoReturn(); } } while (0)
1008 "How did a DataChannel get queued without the "do { if (!(false)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "How did a DataChannel get queued without the "
"mHasFinishedOpen flag?", "false", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 1009); MOZ_PretendNoReturn(); } } while (0)
1009 "mHasFinishedOpen flag?")do { if (!(false)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "How did a DataChannel get queued without the "
"mHasFinishedOpen flag?", "false", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 1009); MOZ_PretendNoReturn(); } } while (0)
;
1010 }
1011 }
1012}
1013
1014void DataChannelConnection::SctpDtlsInput(const std::string& aTransportId,
1015 const MediaPacket& packet) {
1016 MutexAutoLock lock(mLock);
1017 if ((packet.type() != MediaPacket::SCTP) || (mTransportId != aTransportId)) {
1018 return;
1019 }
1020
1021 if (MOZ_LOG_TEST(gSCTPLog, LogLevel::Debug)(__builtin_expect(!!(mozilla::detail::log_test(gSCTPLog, LogLevel
::Debug)), 0))
) {
1022 char* buf;
1023
1024 if ((buf = usrsctp_dumppacket((void*)packet.data(), packet.len(),
1025 SCTP_DUMP_INBOUND0)) != nullptr) {
1026 SCTP_LOG(("%s", buf))do { const ::mozilla::LogModule* moz_real_module = mozilla::gSCTPLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "%s", buf); } } while
(0)
;
1027 usrsctp_freedumpbuffer(buf);
1028 }
1029 }
1030 // Pass the data to SCTP
1031 usrsctp_conninput(reinterpret_cast<void*>(mId), packet.data(), packet.len(),
1032 0);
1033}
1034
1035void DataChannelConnection::SendPacket(std::unique_ptr<MediaPacket>&& packet) {
1036 mSTS->Dispatch(NS_NewRunnableFunction(
1037 "DataChannelConnection::SendPacket",
1038 [this, self = RefPtr<DataChannelConnection>(this),
1039 packet = std::move(packet)]() mutable {
1040 // DC_DEBUG(("%p: SCTP/DTLS sent %ld bytes", this, len));
1041 if (!mTransportId.empty() && mTransportHandler) {
1042 mTransportHandler->SendPacket(mTransportId, std::move(*packet));
1043 }
1044 }));
1045}
1046
1047int DataChannelConnection::SctpDtlsOutput(void* addr, void* buffer,
1048 size_t length, uint8_t tos,
1049 uint8_t set_df) {
1050 if (MOZ_LOG_TEST(gSCTPLog, LogLevel::Debug)(__builtin_expect(!!(mozilla::detail::log_test(gSCTPLog, LogLevel
::Debug)), 0))
) {
1051 char* buf;
1052
1053 if ((buf = usrsctp_dumppacket(buffer, length, SCTP_DUMP_OUTBOUND1)) !=
1054 nullptr) {
1055 SCTP_LOG(("%s", buf))do { const ::mozilla::LogModule* moz_real_module = mozilla::gSCTPLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "%s", buf); } } while
(0)
;
1056 usrsctp_freedumpbuffer(buf);
1057 }
1058 }
1059
1060 // We're async proxying even if on the STSThread because this is called
1061 // with internal SCTP locks held in some cases (such as in usrsctp_connect()).
1062 // SCTP has an option for Apple, on IP connections only, to release at least
1063 // one of the locks before calling a packet output routine; with changes to
1064 // the underlying SCTP stack this might remove the need to use an async proxy.
1065 std::unique_ptr<MediaPacket> packet(new MediaPacket);
1066 packet->SetType(MediaPacket::SCTP);
1067 packet->Copy(static_cast<const uint8_t*>(buffer), length);
1068
1069 if (NS_IsMainThread() && mDeferSend) {
1070 mDeferredSend.emplace_back(std::move(packet));
1071 return 0;
1072 }
1073
1074 SendPacket(std::move(packet));
1075 return 0; // cheat! Packets can always be dropped later anyways
1076}
1077#endif
1078
1079DataChannel* DataChannelConnection::FindChannelByStream(uint16_t stream) {
1080 return mChannels.Get(stream).get();
1081}
1082
1083uint16_t DataChannelConnection::FindFreeStream() const {
1084 ASSERT_WEBRTC(NS_IsMainThread())do { static_assert( mozilla::detail::AssertionConditionType<
decltype((NS_IsMainThread()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((NS_IsMainThread())))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("(NS_IsMainThread())"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 1084); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(NS_IsMainThread())"
")"); do { *((volatile int*)__null) = 1084; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1085 uint16_t i, limit;
1086
1087 limit = MAX_NUM_STREAMS(2048);
1088
1089 MOZ_ASSERT(mAllocateEven.isSome())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mAllocateEven.isSome())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mAllocateEven.isSome()))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("mAllocateEven.isSome()"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 1089); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mAllocateEven.isSome()"
")"); do { *((volatile int*)__null) = 1089; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1090 for (i = (*mAllocateEven ? 0 : 1); i < limit; i += 2) {
1091 if (mChannels.Get(i)) {
1092 continue;
1093 }
1094
1095 // Verify it's not still in the process of closing
1096 size_t j;
1097 for (j = 0; j < mStreamsResetting.Length(); ++j) {
1098 if (mStreamsResetting[j] == i) {
1099 break;
1100 }
1101 }
1102
1103 if (j == mStreamsResetting.Length()) {
1104 return i;
1105 }
1106 }
1107 return INVALID_STREAM(0xFFFF);
1108}
1109
1110uint32_t DataChannelConnection::UpdateCurrentStreamIndex() {
1111 RefPtr<DataChannel> channel = mChannels.GetNextChannel(mCurrentStream);
1112 if (!channel) {
1113 mCurrentStream = 0;
1114 } else {
1115 mCurrentStream = channel->mStream;
1116 }
1117 return mCurrentStream;
1118}
1119
1120uint32_t DataChannelConnection::GetCurrentStreamIndex() {
1121 if (!mChannels.Get(mCurrentStream)) {
1122 // The stream muse have been removed, reset
1123 DC_DEBUG(("Reset mCurrentChannel"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Reset mCurrentChannel"
); } } while (0)
;
1124 mCurrentStream = 0;
1125 }
1126 return mCurrentStream;
1127}
1128
1129bool DataChannelConnection::RequestMoreStreams(int32_t aNeeded) {
1130 struct sctp_status status = {};
1131 struct sctp_add_streams sas = {};
1132 uint32_t outStreamsNeeded;
1133 socklen_t len;
1134
1135 if (aNeeded + mNegotiatedIdLimit > MAX_NUM_STREAMS(2048)) {
1136 aNeeded = MAX_NUM_STREAMS(2048) - mNegotiatedIdLimit;
1137 }
1138 if (aNeeded <= 0) {
1139 return false;
1140 }
1141
1142 len = (socklen_t)sizeof(struct sctp_status);
1143 if (usrsctp_getsockopt(mMasterSocket, IPPROTO_SCTPIPPROTO_SCTP, SCTP_STATUS0x00000100, &status,
1144 &len) < 0) {
1145 DC_ERROR(("***failed: getsockopt SCTP_STATUS"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "***failed: getsockopt SCTP_STATUS"
); } } while (0)
;
1146 return false;
1147 }
1148 outStreamsNeeded = aNeeded; // number to add
1149
1150 // Note: if multiple channel opens happen when we don't have enough space,
1151 // we'll call RequestMoreStreams() multiple times
1152 sas.sas_instrms = 0;
1153 sas.sas_outstrms = (uint16_t)outStreamsNeeded; /* XXX error handling */
1154 // Doesn't block, we get an event when it succeeds or fails
1155 if (usrsctp_setsockopt(mMasterSocket, IPPROTO_SCTPIPPROTO_SCTP, SCTP_ADD_STREAMS0x00000903, &sas,
1156 (socklen_t)sizeof(struct sctp_add_streams)) < 0) {
1157 if (errno(*__errno_location ()) == EALREADY114) {
1158 DC_DEBUG(("Already have %u output streams", outStreamsNeeded))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Already have %u output streams"
, outStreamsNeeded); } } while (0)
;
1159 return true;
1160 }
1161
1162 DC_ERROR(("***failed: setsockopt ADD errno=%d", errno))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "***failed: setsockopt ADD errno=%d"
, (*__errno_location ())); } } while (0)
;
1163 return false;
1164 }
1165 DC_DEBUG(("Requested %u more streams", outStreamsNeeded))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Requested %u more streams"
, outStreamsNeeded); } } while (0)
;
1166 // We add to mNegotiatedIdLimit when we get a SCTP_STREAM_CHANGE_EVENT and the
1167 // values are larger than mNegotiatedIdLimit
1168 return true;
1169}
1170
1171// Returns a POSIX error code.
1172int DataChannelConnection::SendControlMessage(const uint8_t* data, uint32_t len,
1173 uint16_t stream) {
1174 struct sctp_sendv_spa info = {};
1175
1176 // General flags
1177 info.sendv_flags = SCTP_SEND_SNDINFO_VALID0x00000001;
1178
1179 // Set stream identifier, protocol identifier and flags
1180 info.sendv_sndinfo.snd_sid = stream;
1181 info.sendv_sndinfo.snd_flags = SCTP_EOR0x2000;
1182 info.sendv_sndinfo.snd_ppid = htonl(DATA_CHANNEL_PPID_CONTROL)__bswap_32 (50);
1183
1184 // Create message instance and send
1185 // Note: Main-thread IO, but doesn't block
1186#if (UINT32_MAX(4294967295U) > SIZE_MAX(18446744073709551615UL))
1187 if (len > SIZE_MAX(18446744073709551615UL)) {
1188 return EMSGSIZE90;
1189 }
1190#endif
1191 OutgoingMsg msg(info, Span(data, len));
1192 bool buffered;
1193 int error = SendMsgInternalOrBuffer(mBufferedControl, msg, buffered, nullptr);
1194
1195 // Set pending type (if buffered)
1196 if (!error && buffered && mPendingType == PendingType::None) {
1197 mPendingType = PendingType::Dcep;
1198 }
1199 return error;
1200}
1201
1202// Returns a POSIX error code.
1203int DataChannelConnection::SendOpenAckMessage(uint16_t stream) {
1204 struct rtcweb_datachannel_ack ack = {};
1205 ack.msg_type = DATA_CHANNEL_ACK2;
1206
1207 return SendControlMessage((const uint8_t*)&ack, sizeof(ack), stream);
1208}
1209
1210// Returns a POSIX error code.
1211int DataChannelConnection::SendOpenRequestMessage(
1212 const nsACString& label, const nsACString& protocol, uint16_t stream,
1213 bool unordered, DataChannelReliabilityPolicy prPolicy, uint32_t prValue) {
1214 const size_t label_len = label.Length(); // not including nul
1215 const size_t proto_len = protocol.Length(); // not including nul
1216 // careful - request struct include one char for the label
1217 const size_t req_size = sizeof(struct rtcweb_datachannel_open_request) - 1 +
1218 label_len + proto_len;
1219 UniqueFreePtr<struct rtcweb_datachannel_open_request> req(
1220 (struct rtcweb_datachannel_open_request*)moz_xmalloc(req_size));
1221
1222 memset(req.get(), 0, req_size);
1223 req->msg_type = DATA_CHANNEL_OPEN_REQUEST3;
1224 switch (prPolicy) {
1225 case DataChannelReliabilityPolicy::Reliable:
1226 req->channel_type = DATA_CHANNEL_RELIABLE0x00;
1227 break;
1228 case DataChannelReliabilityPolicy::LimitedLifetime:
1229 req->channel_type = DATA_CHANNEL_PARTIAL_RELIABLE_TIMED0x02;
1230 break;
1231 case DataChannelReliabilityPolicy::LimitedRetransmissions:
1232 req->channel_type = DATA_CHANNEL_PARTIAL_RELIABLE_REXMIT0x01;
1233 break;
1234 default:
1235 return EINVAL22;
1236 }
1237 if (unordered) {
1238 // Per the current types, all differ by 0x80 between ordered and unordered
1239 req->channel_type |=
1240 0x80; // NOTE: be careful if new types are added in the future
1241 }
1242
1243 req->reliability_param = htonl(prValue)__bswap_32 (prValue);
1244 req->priority = htons(0)__bswap_16 (0); /* XXX: add support */
1245 req->label_length = htons(label_len)__bswap_16 (label_len);
1246 req->protocol_length = htons(proto_len)__bswap_16 (proto_len);
1247 memcpy(&req->label[0], PromiseFlatCStringTPromiseFlatString<char>(label).get(), label_len);
1248 memcpy(&req->label[label_len], PromiseFlatCStringTPromiseFlatString<char>(protocol).get(), proto_len);
1249
1250 // TODO: req_size is an int... that looks hairy
1251 int error = SendControlMessage((const uint8_t*)req.get(), req_size, stream);
1252 return error;
1253}
1254
1255// XXX This should use a separate thread (outbound queue) which should
1256// select() to know when to *try* to send data to the socket again.
1257// Alternatively, it can use a timeout, but that's guaranteed to be wrong
1258// (just not sure in what direction). We could re-implement NSPR's
1259// PR_POLL_WRITE/etc handling... with a lot of work.
1260
1261// Better yet, use the SCTP stack's notifications on buffer state to avoid
1262// filling the SCTP's buffers.
1263
1264// returns if we're still blocked (true)
1265bool DataChannelConnection::SendDeferredMessages() {
1266 RefPtr<DataChannel> channel; // we may null out the refs to this
1267
1268 // This may block while something is modifying channels, but should not block
1269 // for IO
1270 ASSERT_WEBRTC(!NS_IsMainThread())do { static_assert( mozilla::detail::AssertionConditionType<
decltype((!NS_IsMainThread()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((!NS_IsMainThread())))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("(!NS_IsMainThread())"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 1270); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(!NS_IsMainThread())"
")"); do { *((volatile int*)__null) = 1270; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1271 mLock.AssertCurrentThreadOwns();
1272
1273 DC_DEBUG(("SendDeferredMessages called, pending type: %s",do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "SendDeferredMessages called, pending type: %s"
, ToString(mPendingType)); } } while (0)
1274 ToString(mPendingType)))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "SendDeferredMessages called, pending type: %s"
, ToString(mPendingType)); } } while (0)
;
1275 if (mPendingType == PendingType::None) {
1276 return false;
1277 }
1278
1279 // Send pending control messages
1280 // Note: If ndata is not active, check if DCEP messages are currently
1281 // outstanding. These need to
1282 // be sent first before other streams can be used for sending.
1283 if (!mBufferedControl.IsEmpty() &&
1284 (mSendInterleaved || mPendingType == PendingType::Dcep)) {
1285 if (SendBufferedMessages(mBufferedControl, nullptr)) {
1286 return true;
1287 }
1288
1289 // Note: There may or may not be pending data messages
1290 mPendingType = PendingType::Data;
1291 }
1292
1293 bool blocked = false;
1294 uint32_t i = GetCurrentStreamIndex();
1295 uint32_t end = i;
1296 do {
1297 channel = mChannels.Get(i);
1298 if (!channel) {
1299 continue;
1300 }
1301
1302 // Note that `channel->mConnection` is `this`. This is just here to satisfy
1303 // the thread safety annotations on DataChannel.
1304 channel->mConnection->mLock.AssertCurrentThreadOwns();
1305 // Should already be cleared if closing/closed
1306 if (channel->mBufferedData.IsEmpty()) {
1307 i = UpdateCurrentStreamIndex();
1308 continue;
1309 }
1310
1311 // Send buffered data messages
1312 // Warning: This will fail in case ndata is inactive and a previously
1313 // deallocated data channel has not been closed properly. If you
1314 // ever see that no messages can be sent on any channel, this is
1315 // likely the cause (an explicit EOR message partially sent whose
1316 // remaining chunks are still being waited for).
1317 size_t written = 0;
1318 mDeferSend = true;
1319 blocked = SendBufferedMessages(channel->mBufferedData, &written);
1320 mDeferSend = false;
1321 if (written) {
1322 channel->DecrementBufferedAmount(written);
1323 }
1324
1325 for (auto&& packet : mDeferredSend) {
1326 MOZ_ASSERT(written)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(written)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(written))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("written", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 1326); AnnotateMozCrashReason("MOZ_ASSERT" "(" "written" ")"
); do { *((volatile int*)__null) = 1326; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1327 SendPacket(std::move(packet));
1328 }
1329 mDeferredSend.clear();
1330
1331 // Update current stream index
1332 // Note: If ndata is not active, the outstanding data messages on this
1333 // stream need to be sent first before other streams can be used for
1334 // sending.
1335 if (mSendInterleaved || !blocked) {
1336 i = UpdateCurrentStreamIndex();
1337 }
1338 } while (!blocked && i != end);
1339
1340 if (!blocked) {
1341 mPendingType =
1342 mBufferedControl.IsEmpty() ? PendingType::None : PendingType::Dcep;
1343 }
1344 return blocked;
1345}
1346
1347// buffer MUST have at least one item!
1348// returns if we're still blocked (true)
1349bool DataChannelConnection::SendBufferedMessages(
1350 nsTArray<UniquePtr<BufferedOutgoingMsg>>& buffer, size_t* aWritten) {
1351 mLock.AssertCurrentThreadOwns();
1352 do {
1353 // Re-send message
1354 int error = SendMsgInternal(*buffer[0], aWritten);
1355 switch (error) {
1356 case 0:
1357 buffer.RemoveElementAt(0);
1358 break;
1359 case EAGAIN11:
1360#if (EAGAIN11 != EWOULDBLOCK11)
1361 case EWOULDBLOCK11:
1362#endif
1363 return true;
1364 default:
1365 buffer.RemoveElementAt(0);
1366 DC_ERROR(("error on sending: %d", error))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "error on sending: %d"
, error); } } while (0)
;
1367 break;
1368 }
1369 } while (!buffer.IsEmpty());
1370
1371 return false;
1372}
1373
1374// Caller must ensure that length <= SIZE_MAX
1375void DataChannelConnection::HandleOpenRequestMessage(
1376 const struct rtcweb_datachannel_open_request* req, uint32_t length,
1377 uint16_t stream) {
1378 RefPtr<DataChannel> channel;
1379 uint32_t prValue;
1380 DataChannelReliabilityPolicy prPolicy;
1381
1382 ASSERT_WEBRTC(!NS_IsMainThread())do { static_assert( mozilla::detail::AssertionConditionType<
decltype((!NS_IsMainThread()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((!NS_IsMainThread())))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("(!NS_IsMainThread())"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 1382); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(!NS_IsMainThread())"
")"); do { *((volatile int*)__null) = 1382; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
1383 mLock.AssertCurrentThreadOwns();
1384
1385 const size_t requiredLength = (sizeof(*req) - 1) + ntohs(req->label_length)__bswap_16 (req->label_length) +
1386 ntohs(req->protocol_length)__bswap_16 (req->protocol_length);
1387 if (((size_t)length) != requiredLength) {
1388 if (((size_t)length) < requiredLength) {
1389 DC_ERROR(do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "%s: insufficient length: %u, should be %zu. Unable to continue."
, __FUNCTION__, length, requiredLength); } } while (0)
1390 ("%s: insufficient length: %u, should be %zu. Unable to continue.",do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "%s: insufficient length: %u, should be %zu. Unable to continue."
, __FUNCTION__, length, requiredLength); } } while (0)
1391 __FUNCTION__, length, requiredLength))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "%s: insufficient length: %u, should be %zu. Unable to continue."
, __FUNCTION__, length, requiredLength); } } while (0)
;
1392 return;
1393 }
1394 DC_WARN(("%s: Inconsistent length: %u, should be %zu", __FUNCTION__, length,do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Warning)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Warning, "%s: Inconsistent length: %u, should be %zu"
, __FUNCTION__, length, requiredLength); } } while (0)
1395 requiredLength))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Warning)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Warning, "%s: Inconsistent length: %u, should be %zu"
, __FUNCTION__, length, requiredLength); } } while (0)
;
1396 }
1397
1398 DC_DEBUG(("%s: length %u, sizeof(*req) = %zu", __FUNCTION__, length,do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "%s: length %u, sizeof(*req) = %zu"
, __FUNCTION__, length, sizeof(*req)); } } while (0)
1399 sizeof(*req)))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "%s: length %u, sizeof(*req) = %zu"
, __FUNCTION__, length, sizeof(*req)); } } while (0)
;
1400
1401 switch (req->channel_type) {
1402 case DATA_CHANNEL_RELIABLE0x00:
1403 case DATA_CHANNEL_RELIABLE_UNORDERED0x80:
1404 prPolicy = DataChannelReliabilityPolicy::Reliable;
1405 break;
1406 case DATA_CHANNEL_PARTIAL_RELIABLE_REXMIT0x01:
1407 case DATA_CHANNEL_PARTIAL_RELIABLE_REXMIT_UNORDERED0x81:
1408 prPolicy = DataChannelReliabilityPolicy::LimitedRetransmissions;
1409 break;
1410 case DATA_CHANNEL_PARTIAL_RELIABLE_TIMED0x02:
1411 case DATA_CHANNEL_PARTIAL_RELIABLE_TIMED_UNORDERED0x82:
1412 prPolicy = DataChannelReliabilityPolicy::LimitedLifetime;
1413 break;
1414 default:
1415 DC_ERROR(("Unknown channel type %d", req->channel_type))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "Unknown channel type %d"
, req->channel_type); } } while (0)
;
1416 /* XXX error handling */
1417 return;
1418 }
1419 prValue = ntohl(req->reliability_param)__bswap_32 (req->reliability_param);
1420 bool ordered = !(req->channel_type & 0x80);
1421
1422 if ((channel = FindChannelByStream(stream))) {
1423 if (!channel->mNegotiated) {
1424 DC_ERROR(do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "HandleOpenRequestMessage: channel for pre-existing stream "
"%u that was not externally negotiated. JS is lying to us, or "
"there's an id collision.", stream); } } while (0)
1425 ("HandleOpenRequestMessage: channel for pre-existing stream "do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "HandleOpenRequestMessage: channel for pre-existing stream "
"%u that was not externally negotiated. JS is lying to us, or "
"there's an id collision.", stream); } } while (0)
1426 "%u that was not externally negotiated. JS is lying to us, or "do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "HandleOpenRequestMessage: channel for pre-existing stream "
"%u that was not externally negotiated. JS is lying to us, or "
"there's an id collision.", stream); } } while (0)
1427 "there's an id collision.",do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "HandleOpenRequestMessage: channel for pre-existing stream "
"%u that was not externally negotiated. JS is lying to us, or "
"there's an id collision.", stream); } } while (0)
1428 stream))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "HandleOpenRequestMessage: channel for pre-existing stream "
"%u that was not externally negotiated. JS is lying to us, or "
"there's an id collision.", stream); } } while (0)
;
1429 /* XXX: some error handling */
1430 } else {
1431 DC_DEBUG(("Open for externally negotiated channel %u", stream))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Open for externally negotiated channel %u"
, stream); } } while (0)
;
1432 // XXX should also check protocol, maybe label
1433 if (prPolicy != channel->mPrPolicy || prValue != channel->mPrValue ||
1434 ordered != channel->mOrdered) {
1435 DC_WARN(do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Warning)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Warning, "external negotiation mismatch with OpenRequest:"
"channel %u, policy %s/%s, value %u/%u, ordered %d/%d", stream
, ToString(prPolicy), ToString(channel->mPrPolicy), prValue
, channel->mPrValue, static_cast<int>(ordered), static_cast
<int>(channel->mOrdered)); } } while (0)
1436 ("external negotiation mismatch with OpenRequest:"do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Warning)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Warning, "external negotiation mismatch with OpenRequest:"
"channel %u, policy %s/%s, value %u/%u, ordered %d/%d", stream
, ToString(prPolicy), ToString(channel->mPrPolicy), prValue
, channel->mPrValue, static_cast<int>(ordered), static_cast
<int>(channel->mOrdered)); } } while (0)
1437 "channel %u, policy %s/%s, value %u/%u, ordered %d/%d",do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Warning)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Warning, "external negotiation mismatch with OpenRequest:"
"channel %u, policy %s/%s, value %u/%u, ordered %d/%d", stream
, ToString(prPolicy), ToString(channel->mPrPolicy), prValue
, channel->mPrValue, static_cast<int>(ordered), static_cast
<int>(channel->mOrdered)); } } while (0)
1438 stream, ToString(prPolicy), ToString(channel->mPrPolicy), prValue,do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Warning)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Warning, "external negotiation mismatch with OpenRequest:"
"channel %u, policy %s/%s, value %u/%u, ordered %d/%d", stream
, ToString(prPolicy), ToString(channel->mPrPolicy), prValue
, channel->mPrValue, static_cast<int>(ordered), static_cast
<int>(channel->mOrdered)); } } while (0)
1439 channel->mPrValue, static_cast<int>(ordered),do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Warning)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Warning, "external negotiation mismatch with OpenRequest:"
"channel %u, policy %s/%s, value %u/%u, ordered %d/%d", stream
, ToString(prPolicy), ToString(channel->mPrPolicy), prValue
, channel->mPrValue, static_cast<int>(ordered), static_cast
<int>(channel->mOrdered)); } } while (0)
1440 static_cast<int>(channel->mOrdered)))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Warning)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Warning, "external negotiation mismatch with OpenRequest:"
"channel %u, policy %s/%s, value %u/%u, ordered %d/%d", stream
, ToString(prPolicy), ToString(channel->mPrPolicy), prValue
, channel->mPrValue, static_cast<int>(ordered), static_cast
<int>(channel->mOrdered)); } } while (0)
;
1441 }
1442 }
1443 return;
1444 }
1445 if (stream >= mNegotiatedIdLimit) {
1446 DC_ERROR(("%s: stream %u out of bounds (%zu)", __FUNCTION__, stream,do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "%s: stream %u out of bounds (%zu)"
, __FUNCTION__, stream, mNegotiatedIdLimit); } } while (0)
1447 mNegotiatedIdLimit))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "%s: stream %u out of bounds (%zu)"
, __FUNCTION__, stream, mNegotiatedIdLimit); } } while (0)
;
1448 return;
1449 }
1450
1451 nsCString label(
1452 nsDependentCSubstring(&req->label[0], ntohs(req->label_length)__bswap_16 (req->label_length)));
1453 nsCString protocol(nsDependentCSubstring(
1454 &req->label[ntohs(req->label_length)__bswap_16 (req->label_length)], ntohs(req->protocol_length)__bswap_16 (req->protocol_length)));
1455
1456 channel =
1457 new DataChannel(this, stream, DataChannelState::Open, label, protocol,
1458 prPolicy, prValue, ordered, false, nullptr, nullptr);
1459 mChannels.Insert(channel);
1460
1461 DC_DEBUG(("%s: sending ON_CHANNEL_CREATED for %s/%s: %u", __FUNCTION__,do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "%s: sending ON_CHANNEL_CREATED for %s/%s: %u"
, __FUNCTION__, channel->mLabel.get(), channel->mProtocol
.get(), stream); } } while (0)
1462 channel->mLabel.get(), channel->mProtocol.get(), stream))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "%s: sending ON_CHANNEL_CREATED for %s/%s: %u"
, __FUNCTION__, channel->mLabel.get(), channel->mProtocol
.get(), stream); } } while (0)
;
1463 Dispatch(do_AddRef(new DataChannelOnMessageAvailable(
1464 DataChannelOnMessageAvailable::EventType::OnChannelCreated, this,
1465 channel)));
1466
1467 DC_DEBUG(("%s: deferring sending ON_CHANNEL_OPEN for %p", __FUNCTION__,do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "%s: deferring sending ON_CHANNEL_OPEN for %p"
, __FUNCTION__, channel.get()); } } while (0)
1468 channel.get()))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "%s: deferring sending ON_CHANNEL_OPEN for %p"
, __FUNCTION__, channel.get()); } } while (0)
;
1469 channel->AnnounceOpen();
1470
1471 // Note that any message can be buffered; SendOpenAckMessage may
1472 // error later than this check.
1473 const auto error = SendOpenAckMessage(channel->mStream);
1474 if (error) {
1475 DC_ERROR(("SendOpenRequest failed, error = %d", error))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "SendOpenRequest failed, error = %d"
, error); } } while (0)
;
1476 Dispatch(NS_NewRunnableFunction(
1477 "DataChannelConnection::HandleOpenRequestMessage",
1478 [channel, connection = RefPtr<DataChannelConnection>(this)]() {
1479 // Close the channel on failure
1480 connection->Close(channel);
1481 }));
1482 return;
1483 }
1484 DeliverQueuedData(channel->mStream);
1485}
1486
1487// NOTE: the updated spec from the IETF says we should set in-order until we
1488// receive an ACK. That would make this code moot. Keep it for now for
1489// backwards compatibility.
1490void DataChannelConnection::DeliverQueuedData(uint16_t stream) {
1491 mLock.AssertCurrentThreadOwns();
1492
1493 mQueuedData.RemoveElementsBy([stream, this](const auto& dataItem) {
1494 mLock.AssertCurrentThreadOwns();
1495 const bool match = dataItem->mStream == stream;
1496 if (match) {
1497 DC_DEBUG(("Delivering queued data for stream %u, length %zu", stream,do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Delivering queued data for stream %u, length %zu"
, stream, dataItem->mData.Length()); } } while (0)
1498 dataItem->mData.Length()))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Delivering queued data for stream %u, length %zu"
, stream, dataItem->mData.Length()); } } while (0)
;
1499 // Deliver the queued data
1500 HandleDataMessage(dataItem->mData.Elements(), dataItem->mData.Length(),
1501 dataItem->mPpid, dataItem->mStream, dataItem->mFlags);
1502 }
1503 return match;
1504 });
1505}
1506
1507// Caller must ensure that length <= SIZE_MAX
1508void DataChannelConnection::HandleOpenAckMessage(
1509 const struct rtcweb_datachannel_ack* ack, uint32_t length,
1510 uint16_t stream) {
1511 DataChannel* channel;
1512
1513 mLock.AssertCurrentThreadOwns();
1514
1515 channel = FindChannelByStream(stream);
1516 if (NS_WARN_IF(!channel)NS_warn_if_impl(!channel, "!channel", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 1516)
) {
1517 return;
1518 }
1519
1520 DC_DEBUG(("OpenAck received for stream %u, waiting=%d", stream,do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "OpenAck received for stream %u, waiting=%d"
, stream, channel->mWaitingForAck ? 1 : 0); } } while (0)
1521 channel->mWaitingForAck ? 1 : 0))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "OpenAck received for stream %u, waiting=%d"
, stream, channel->mWaitingForAck ? 1 : 0); } } while (0)
;
1522
1523 channel->mWaitingForAck = false;
1524}
1525
1526// Caller must ensure that length <= SIZE_MAX
1527void DataChannelConnection::HandleUnknownMessage(uint32_t ppid, uint32_t length,
1528 uint16_t stream) {
1529 /* XXX: Send an error message? */
1530 DC_ERROR(("unknown DataChannel message received: %u, len %u on stream %d",do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "unknown DataChannel message received: %u, len %u on stream %d"
, ppid, length, stream); } } while (0)
1531 ppid, length, stream))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "unknown DataChannel message received: %u, len %u on stream %d"
, ppid, length, stream); } } while (0)
;
1532 // XXX Log to JS error console if possible
1533}
1534
1535uint8_t DataChannelConnection::BufferMessage(nsACString& recvBuffer,
1536 const void* data, uint32_t length,
1537 uint32_t ppid, int flags) {
1538 const char* buffer = (const char*)data;
1539 uint8_t bufferFlags = 0;
1540
1541 if ((flags & MSG_EORMSG_EOR) && ppid != DATA_CHANNEL_PPID_BINARY_PARTIAL52 &&
1542 ppid != DATA_CHANNEL_PPID_DOMSTRING_PARTIAL54) {
1543 bufferFlags |= DATA_CHANNEL_BUFFER_MESSAGE_FLAGS_COMPLETE0x04;
1544
1545 // Return directly if nothing has been buffered
1546 if (recvBuffer.IsEmpty()) {
1547 return bufferFlags;
1548 }
1549 }
1550
1551 // Ensure it doesn't blow up our buffer
1552 // TODO: Change 'WEBRTC_DATACHANNEL_MAX_MESSAGE_SIZE_LOCAL' to whatever the
1553 // new buffer is capable of holding.
1554 if (((uint64_t)recvBuffer.Length()) + ((uint64_t)length) >
1555 WEBRTC_DATACHANNEL_MAX_MESSAGE_SIZE_LOCAL1073741823) {
1556 bufferFlags |= DATA_CHANNEL_BUFFER_MESSAGE_FLAGS_TOO_LARGE0x01;
1557 return bufferFlags;
1558 }
1559
1560 // Copy & add to receive buffer
1561 recvBuffer.Append(buffer, length);
1562 bufferFlags |= DATA_CHANNEL_BUFFER_MESSAGE_FLAGS_BUFFERED0x02;
1563 return bufferFlags;
1564}
1565
1566void DataChannelConnection::HandleDataMessage(const void* data, size_t length,
1567 uint32_t ppid, uint16_t stream,
1568 int flags) {
1569 DataChannel* channel;
1570 const char* buffer = (const char*)data;
1571
1572 mLock.AssertCurrentThreadOwns();
1573 channel = FindChannelByStream(stream);
1574
1575 // Note: Until we support SIZE_MAX sized messages, we need this check
1576#if (SIZE_MAX(18446744073709551615UL) > UINT32_MAX(4294967295U))
1577 if (length > UINT32_MAX(4294967295U)) {
1578 DC_ERROR(("DataChannel: Cannot handle message of size %zu (max=%" PRIu32do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "DataChannel: Cannot handle message of size %zu (max=%"
"u" ")", length, (4294967295U)); } } while (0)
1579 ")",do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "DataChannel: Cannot handle message of size %zu (max=%"
"u" ")", length, (4294967295U)); } } while (0)
1580 length, UINT32_MAX))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "DataChannel: Cannot handle message of size %zu (max=%"
"u" ")", length, (4294967295U)); } } while (0)
;
1581 CloseLocked(channel);
1582 return;
1583 }
1584#endif
1585 uint32_t data_length = (uint32_t)length;
1586
1587 // XXX A closed channel may trip this... check
1588 // NOTE: the updated spec from the IETF says we should set in-order until we
1589 // receive an ACK. That would make this code moot. Keep it for now for
1590 // backwards compatibility.
1591 if (!channel) {
1592 // In the updated 0-RTT open case, the sender can send data immediately
1593 // after Open, and doesn't set the in-order bit (since we don't have a
1594 // response or ack). Also, with external negotiation, data can come in
1595 // before we're told about the external negotiation. We need to buffer
1596 // data until either a) Open comes in, if the ordering get messed up,
1597 // or b) the app tells us this channel was externally negotiated. When
1598 // these occur, we deliver the data.
1599
1600 // Since this is rare and non-performance, keep a single list of queued
1601 // data messages to deliver once the channel opens.
1602 DC_DEBUG(("Queuing data for stream %u, length %u", stream, data_length))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Queuing data for stream %u, length %u"
, stream, data_length); } } while (0)
;
1603 // Copies data
1604 mQueuedData.AppendElement(new QueuedDataMessage(
1605 stream, ppid, flags, static_cast<const uint8_t*>(data), data_length));
1606 return;
1607 }
1608
1609 // Note that `channel->mConnection` is `this`. This is just here to satisfy
1610 // the thread safety annotations on DataChannel.
1611 channel->mConnection->mLock.AssertCurrentThreadOwns();
1612
1613 // RFC8832: "MUST be sent ordered, ... After the DATA_CHANNEL_ACK **or any
1614 // other message** has been received on the data channel".
1615 // If the channel was opened on this side, and a message is received, this
1616 // indicates that the peer has already received the DATA_CHANNEL_ACK, as the
1617 // channel is ordered initially.
1618 channel->mWaitingForAck = false;
1619
1620 bool is_binary = true;
1621 uint8_t bufferFlags;
1622 DataChannelOnMessageAvailable::EventType type;
1623 const char* info = "";
1624
1625 if (ppid == DATA_CHANNEL_PPID_DOMSTRING_PARTIAL54 ||
1626 ppid == DATA_CHANNEL_PPID_DOMSTRING51 ||
1627 ppid == DATA_CHANNEL_PPID_DOMSTRING_EMPTY56) {
1628 is_binary = false;
1629 }
1630 if (is_binary != channel->mIsRecvBinary && !channel->mRecvBuffer.IsEmpty()) {
1631 NS_WARNING("DataChannel message aborted by fragment type change!")NS_DebugBreak(NS_DEBUG_WARNING, "DataChannel message aborted by fragment type change!"
, nullptr, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 1631)
;
1632 // TODO: Maybe closing would be better as this is a hard to detect protocol
1633 // violation?
1634 channel->mRecvBuffer.Truncate(0);
1635 }
1636 channel->mIsRecvBinary = is_binary;
1637
1638 // Remaining chunks of previously truncated message (due to the buffer being
1639 // full)?
1640 if (channel->mClosingTooLarge) {
1641 DC_ERROR(do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "DataChannel: Ignoring partial message of length %u, buffer full and "
"closing", data_length); } } while (0)
1642 ("DataChannel: Ignoring partial message of length %u, buffer full and "do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "DataChannel: Ignoring partial message of length %u, buffer full and "
"closing", data_length); } } while (0)
1643 "closing",do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "DataChannel: Ignoring partial message of length %u, buffer full and "
"closing", data_length); } } while (0)
1644 data_length))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "DataChannel: Ignoring partial message of length %u, buffer full and "
"closing", data_length); } } while (0)
;
1645 // Only unblock if unordered
1646 if (!channel->mOrdered && (flags & MSG_EORMSG_EOR)) {
1647 channel->mClosingTooLarge = false;
1648 }
1649 }
1650
1651 // Buffer message until complete
1652 bufferFlags =
1653 BufferMessage(channel->mRecvBuffer, buffer, data_length, ppid, flags);
1654 if (bufferFlags & DATA_CHANNEL_BUFFER_MESSAGE_FLAGS_TOO_LARGE0x01) {
1655 DC_ERROR(do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "DataChannel: Buffered message would become too large to handle, "
"closing channel"); } } while (0)
1656 ("DataChannel: Buffered message would become too large to handle, "do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "DataChannel: Buffered message would become too large to handle, "
"closing channel"); } } while (0)
1657 "closing channel"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "DataChannel: Buffered message would become too large to handle, "
"closing channel"); } } while (0)
;
1658 channel->mRecvBuffer.Truncate(0);
1659 channel->mClosingTooLarge = true;
1660 CloseLocked(channel);
1661 return;
1662 }
1663 if (!(bufferFlags & DATA_CHANNEL_BUFFER_MESSAGE_FLAGS_COMPLETE0x04)) {
1664 DC_DEBUG(do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "DataChannel: Partial %s message of length %u (total %zu) on channel "
"id %u", is_binary ? "binary" : "string", data_length, channel
->mRecvBuffer.Length(), channel->mStream); } } while (0
)
1665 ("DataChannel: Partial %s message of length %u (total %zu) on channel "do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "DataChannel: Partial %s message of length %u (total %zu) on channel "
"id %u", is_binary ? "binary" : "string", data_length, channel
->mRecvBuffer.Length(), channel->mStream); } } while (0
)
1666 "id %u",do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "DataChannel: Partial %s message of length %u (total %zu) on channel "
"id %u", is_binary ? "binary" : "string", data_length, channel
->mRecvBuffer.Length(), channel->mStream); } } while (0
)
1667 is_binary ? "binary" : "string", data_length,do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "DataChannel: Partial %s message of length %u (total %zu) on channel "
"id %u", is_binary ? "binary" : "string", data_length, channel
->mRecvBuffer.Length(), channel->mStream); } } while (0
)
1668 channel->mRecvBuffer.Length(), channel->mStream))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "DataChannel: Partial %s message of length %u (total %zu) on channel "
"id %u", is_binary ? "binary" : "string", data_length, channel
->mRecvBuffer.Length(), channel->mStream); } } while (0
)
;
1669 return; // Not ready to notify application
1670 }
1671 if (bufferFlags & DATA_CHANNEL_BUFFER_MESSAGE_FLAGS_BUFFERED0x02) {
1672 data_length = channel->mRecvBuffer.Length();
1673 }
1674
1675 // Complain about large messages (only complain - we can handle it)
1676 if (data_length > WEBRTC_DATACHANNEL_MAX_MESSAGE_SIZE_LOCAL1073741823) {
1677 DC_WARN(do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Warning)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Warning, "DataChannel: Received message of length %u is > announced maximum "
"message size (%u)", data_length, 1073741823); } } while (0)
1678 ("DataChannel: Received message of length %u is > announced maximum "do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Warning)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Warning, "DataChannel: Received message of length %u is > announced maximum "
"message size (%u)", data_length, 1073741823); } } while (0)
1679 "message size (%u)",do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Warning)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Warning, "DataChannel: Received message of length %u is > announced maximum "
"message size (%u)", data_length, 1073741823); } } while (0)
1680 data_length, WEBRTC_DATACHANNEL_MAX_MESSAGE_SIZE_LOCAL))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Warning)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Warning, "DataChannel: Received message of length %u is > announced maximum "
"message size (%u)", data_length, 1073741823); } } while (0)
;
1681 }
1682
1683 bool is_empty = false;
1684
1685 switch (ppid) {
1686 case DATA_CHANNEL_PPID_DOMSTRING51:
1687 DC_DEBUG(do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "DataChannel: Received string message of length %u on channel %u"
, data_length, channel->mStream); } } while (0)
1688 ("DataChannel: Received string message of length %u on channel %u",do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "DataChannel: Received string message of length %u on channel %u"
, data_length, channel->mStream); } } while (0)
1689 data_length, channel->mStream))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "DataChannel: Received string message of length %u on channel %u"
, data_length, channel->mStream); } } while (0)
;
1690 type = DataChannelOnMessageAvailable::EventType::OnDataString;
1691 if (bufferFlags & DATA_CHANNEL_BUFFER_MESSAGE_FLAGS_BUFFERED0x02) {
1692 info = " (string fragmented)";
1693 }
1694 // else send using recvData normally
1695
1696 // WebSockets checks IsUTF8() here; we can try to deliver it
1697 break;
1698
1699 case DATA_CHANNEL_PPID_DOMSTRING_EMPTY56:
1700 DC_DEBUG(do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "DataChannel: Received empty string message of length %u on channel "
"%u", data_length, channel->mStream); } } while (0)
1701 ("DataChannel: Received empty string message of length %u on channel "do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "DataChannel: Received empty string message of length %u on channel "
"%u", data_length, channel->mStream); } } while (0)
1702 "%u",do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "DataChannel: Received empty string message of length %u on channel "
"%u", data_length, channel->mStream); } } while (0)
1703 data_length, channel->mStream))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "DataChannel: Received empty string message of length %u on channel "
"%u", data_length, channel->mStream); } } while (0)
;
1704 type = DataChannelOnMessageAvailable::EventType::OnDataString;
1705 if (bufferFlags & DATA_CHANNEL_BUFFER_MESSAGE_FLAGS_BUFFERED0x02) {
1706 info = " (string fragmented)";
1707 }
1708 is_empty = true;
1709 break;
1710
1711 case DATA_CHANNEL_PPID_BINARY53:
1712 DC_DEBUG(do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "DataChannel: Received binary message of length %u on channel id %u"
, data_length, channel->mStream); } } while (0)
1713 ("DataChannel: Received binary message of length %u on channel id %u",do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "DataChannel: Received binary message of length %u on channel id %u"
, data_length, channel->mStream); } } while (0)
1714 data_length, channel->mStream))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "DataChannel: Received binary message of length %u on channel id %u"
, data_length, channel->mStream); } } while (0)
;
1715 type = DataChannelOnMessageAvailable::EventType::OnDataBinary;
1716 if (bufferFlags & DATA_CHANNEL_BUFFER_MESSAGE_FLAGS_BUFFERED0x02) {
1717 info = " (binary fragmented)";
1718 }
1719
1720 // else send using recvData normally
1721 break;
1722
1723 case DATA_CHANNEL_PPID_BINARY_EMPTY57:
1724 DC_DEBUG(do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "DataChannel: Received empty binary message of length %u on channel "
"id %u", data_length, channel->mStream); } } while (0)
1725 ("DataChannel: Received empty binary message of length %u on channel "do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "DataChannel: Received empty binary message of length %u on channel "
"id %u", data_length, channel->mStream); } } while (0)
1726 "id %u",do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "DataChannel: Received empty binary message of length %u on channel "
"id %u", data_length, channel->mStream); } } while (0)
1727 data_length, channel->mStream))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "DataChannel: Received empty binary message of length %u on channel "
"id %u", data_length, channel->mStream); } } while (0)
;
1728 type = DataChannelOnMessageAvailable::EventType::OnDataBinary;
1729 if (bufferFlags & DATA_CHANNEL_BUFFER_MESSAGE_FLAGS_BUFFERED0x02) {
1730 info = " (binary fragmented)";
1731 }
1732 is_empty = true;
1733 break;
1734
1735 default:
1736 NS_ERROR("Unknown data PPID")do { NS_DebugBreak(NS_DEBUG_ASSERTION, "Unknown data PPID", "Error"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 1736); MOZ_PretendNoReturn(); } while (0)
;
1737 DC_ERROR(("Unknown data PPID %" PRIu32, ppid))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "Unknown data PPID %"
"u", ppid); } } while (0)
;
1738 return;
1739 }
1740
1741 channel->WithTrafficCounters(
1742 [&data_length](DataChannel::TrafficCounters& counters) {
1743 counters.mMessagesReceived++;
1744 counters.mBytesReceived += data_length;
1745 });
1746
1747 // Notify onmessage
1748 DC_DEBUG(do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "%s: sending %s%s for %p"
, __FUNCTION__, ToString(type), info, channel); } } while (0)
1749 ("%s: sending %s%s for %p", __FUNCTION__, ToString(type), info, channel))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "%s: sending %s%s for %p"
, __FUNCTION__, ToString(type), info, channel); } } while (0)
;
1750 if (bufferFlags & DATA_CHANNEL_BUFFER_MESSAGE_FLAGS_BUFFERED0x02) {
1751 channel->SendOrQueue(new DataChannelOnMessageAvailable(
1752 type, this, channel, channel->mRecvBuffer));
1753 channel->mRecvBuffer.Truncate(0);
1754 } else {
1755 nsAutoCString recvData(is_empty ? "" : buffer,
1756 is_empty ? 0 : data_length); // allocates >64
1757 channel->SendOrQueue(
1758 new DataChannelOnMessageAvailable(type, this, channel, recvData));
1759 }
1760}
1761
1762void DataChannelConnection::HandleDCEPMessage(const void* buffer, size_t length,
1763 uint32_t ppid, uint16_t stream,
1764 int flags) {
1765 const struct rtcweb_datachannel_open_request* req;
1766 const struct rtcweb_datachannel_ack* ack;
1767
1768 // Note: Until we support SIZE_MAX sized messages, we need this check
1769#if (SIZE_MAX(18446744073709551615UL) > UINT32_MAX(4294967295U))
1770 if (length > UINT32_MAX(4294967295U)) {
1771 DC_ERROR(("DataChannel: Cannot handle message of size %zu (max=%u)", length,do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "DataChannel: Cannot handle message of size %zu (max=%u)"
, length, (4294967295U)); } } while (0)
1772 UINT32_MAX))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "DataChannel: Cannot handle message of size %zu (max=%u)"
, length, (4294967295U)); } } while (0)
;
1773 Stop();
1774 return;
1775 }
1776#endif
1777 uint32_t data_length = (uint32_t)length;
1778
1779 mLock.AssertCurrentThreadOwns();
1780
1781 // Buffer message until complete
1782 const uint8_t bufferFlags =
1783 BufferMessage(mRecvBuffer, buffer, data_length, ppid, flags);
1784 if (bufferFlags & DATA_CHANNEL_BUFFER_MESSAGE_FLAGS_TOO_LARGE0x01) {
1785 DC_ERROR(do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "DataChannel: Buffered message would become too large to handle, "
"closing connection"); } } while (0)
1786 ("DataChannel: Buffered message would become too large to handle, "do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "DataChannel: Buffered message would become too large to handle, "
"closing connection"); } } while (0)
1787 "closing connection"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "DataChannel: Buffered message would become too large to handle, "
"closing connection"); } } while (0)
;
1788 mRecvBuffer.Truncate(0);
1789 Stop();
1790 return;
1791 }
1792 if (!(bufferFlags & DATA_CHANNEL_BUFFER_MESSAGE_FLAGS_COMPLETE0x04)) {
1793 DC_DEBUG(("Buffered partial DCEP message of length %u", data_length))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Buffered partial DCEP message of length %u"
, data_length); } } while (0)
;
1794 return;
1795 }
1796 if (bufferFlags & DATA_CHANNEL_BUFFER_MESSAGE_FLAGS_BUFFERED0x02) {
1797 buffer = reinterpret_cast<const void*>(mRecvBuffer.BeginReading());
1798 data_length = mRecvBuffer.Length();
1799 }
1800
1801 req = static_cast<const struct rtcweb_datachannel_open_request*>(buffer);
1802 DC_DEBUG(("Handling DCEP message of length %u", data_length))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Handling DCEP message of length %u"
, data_length); } } while (0)
;
1803
1804 // Ensure minimum message size (ack is the smallest DCEP message)
1805 if ((size_t)data_length < sizeof(*ack)) {
1806 DC_WARN(("Ignored invalid DCEP message (too short)"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Warning)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Warning, "Ignored invalid DCEP message (too short)"
); } } while (0)
;
1807 return;
1808 }
1809
1810 switch (req->msg_type) {
1811 case DATA_CHANNEL_OPEN_REQUEST3:
1812 // structure includes a possibly-unused char label[1] (in a packed
1813 // structure)
1814 if (NS_WARN_IF((size_t)data_length < sizeof(*req) - 1)NS_warn_if_impl((size_t)data_length < sizeof(*req) - 1, "(size_t)data_length < sizeof(*req) - 1"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 1814)
) {
1815 return;
1816 }
1817
1818 HandleOpenRequestMessage(req, data_length, stream);
1819 break;
1820 case DATA_CHANNEL_ACK2:
1821 // >= sizeof(*ack) checked above
1822
1823 ack = static_cast<const struct rtcweb_datachannel_ack*>(buffer);
1824 HandleOpenAckMessage(ack, data_length, stream);
1825 break;
1826 default:
1827 HandleUnknownMessage(ppid, data_length, stream);
1828 break;
1829 }
1830
1831 // Reset buffer
1832 mRecvBuffer.Truncate(0);
1833}
1834
1835void DataChannelConnection::HandleMessage(const void* buffer, size_t length,
1836 uint32_t ppid, uint16_t stream,
1837 int flags) {
1838 mLock.AssertCurrentThreadOwns();
1839
1840 switch (ppid) {
1841 case DATA_CHANNEL_PPID_CONTROL50:
1842 HandleDCEPMessage(buffer, length, ppid, stream, flags);
1843 break;
1844 case DATA_CHANNEL_PPID_DOMSTRING_PARTIAL54:
1845 case DATA_CHANNEL_PPID_DOMSTRING51:
1846 case DATA_CHANNEL_PPID_DOMSTRING_EMPTY56:
1847 case DATA_CHANNEL_PPID_BINARY_PARTIAL52:
1848 case DATA_CHANNEL_PPID_BINARY53:
1849 case DATA_CHANNEL_PPID_BINARY_EMPTY57:
1850 HandleDataMessage(buffer, length, ppid, stream, flags);
1851 break;
1852 default:
1853 DC_ERROR((do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "Unhandled message of length %zu PPID %u on stream %u received (%s)."
, length, ppid, stream, (flags & MSG_EOR) ? "complete" : "partial"
); } } while (0)
1854 "Unhandled message of length %zu PPID %u on stream %u received (%s).",do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "Unhandled message of length %zu PPID %u on stream %u received (%s)."
, length, ppid, stream, (flags & MSG_EOR) ? "complete" : "partial"
); } } while (0)
1855 length, ppid, stream, (flags & MSG_EOR) ? "complete" : "partial"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "Unhandled message of length %zu PPID %u on stream %u received (%s)."
, length, ppid, stream, (flags & MSG_EOR) ? "complete" : "partial"
); } } while (0)
;
1856 break;
1857 }
1858}
1859
1860void DataChannelConnection::HandleAssociationChangeEvent(
1861 const struct sctp_assoc_change* sac) {
1862 mLock.AssertCurrentThreadOwns();
1863
1864 uint32_t i, n;
1865 DataChannelConnectionState state = GetState();
1866 switch (sac->sac_state) {
1
Control jumps to 'case 1:' at line 1867
1867 case SCTP_COMM_UP0x0001:
1868 DC_DEBUG(("Association change: SCTP_COMM_UP"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Association change: SCTP_COMM_UP"
); } } while (0)
;
2
Assuming the condition is true
3
Taking false branch
4
Loop condition is false. Exiting loop
1869 if (state == DataChannelConnectionState::Connecting) {
5
Assuming 'state' is equal to Connecting
6
Taking true branch
1870 mSocket = mMasterSocket;
1871 SetState(DataChannelConnectionState::Open);
1872
1873 DC_DEBUG(("Negotiated number of incoming streams: %" PRIu16,do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Negotiated number of incoming streams: %"
"u", sac->sac_inbound_streams); } } while (0)
7
Assuming the condition is true
8
Taking false branch
9
Loop condition is false. Exiting loop
1874 sac->sac_inbound_streams))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Negotiated number of incoming streams: %"
"u", sac->sac_inbound_streams); } } while (0)
;
1875 DC_DEBUG(("Negotiated number of outgoing streams: %" PRIu16,do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Negotiated number of outgoing streams: %"
"u", sac->sac_outbound_streams); } } while (0)
10
Assuming the condition is true
11
Taking false branch
12
Loop condition is false. Exiting loop
1876 sac->sac_outbound_streams))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Negotiated number of outgoing streams: %"
"u", sac->sac_outbound_streams); } } while (0)
;
1877 mNegotiatedIdLimit =
1878 std::max(mNegotiatedIdLimit,
1879 static_cast<size_t>(std::max(sac->sac_outbound_streams,
1880 sac->sac_inbound_streams)));
1881
1882 Dispatch(do_AddRef(new DataChannelOnMessageAvailable(
1883 DataChannelOnMessageAvailable::EventType::OnConnection, this)));
1884 DC_DEBUG(("DTLS connect() succeeded! Entering connected mode"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "DTLS connect() succeeded! Entering connected mode"
); } } while (0)
;
13
Assuming the condition is true
14
Taking false branch
15
Loop condition is false. Exiting loop
1885
1886 // Open any streams pending...
1887 ProcessQueuedOpens();
16
Calling 'DataChannelConnection::ProcessQueuedOpens'
1888
1889 } else if (state == DataChannelConnectionState::Open) {
1890 DC_DEBUG(("DataConnection Already OPEN"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "DataConnection Already OPEN"
); } } while (0)
;
1891 } else {
1892 DC_ERROR(("Unexpected state: %s", ToString(state)))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "Unexpected state: %s"
, ToString(state)); } } while (0)
;
1893 }
1894 break;
1895 case SCTP_COMM_LOST0x0002:
1896 DC_DEBUG(("Association change: SCTP_COMM_LOST"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Association change: SCTP_COMM_LOST"
); } } while (0)
;
1897 // This association is toast, so also close all the channels -- from
1898 // mainthread!
1899 Stop();
1900 break;
1901 case SCTP_RESTART0x0003:
1902 DC_DEBUG(("Association change: SCTP_RESTART"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Association change: SCTP_RESTART"
); } } while (0)
;
1903 break;
1904 case SCTP_SHUTDOWN_COMP0x0004:
1905 DC_DEBUG(("Association change: SCTP_SHUTDOWN_COMP"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Association change: SCTP_SHUTDOWN_COMP"
); } } while (0)
;
1906 Stop();
1907 break;
1908 case SCTP_CANT_STR_ASSOC0x0005:
1909 DC_DEBUG(("Association change: SCTP_CANT_STR_ASSOC"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Association change: SCTP_CANT_STR_ASSOC"
); } } while (0)
;
1910 break;
1911 default:
1912 DC_DEBUG(("Association change: UNKNOWN"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Association change: UNKNOWN"
); } } while (0)
;
1913 break;
1914 }
1915 DC_DEBUG(("Association change: streams (in/out) = (%u/%u)",do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Association change: streams (in/out) = (%u/%u)"
, sac->sac_inbound_streams, sac->sac_outbound_streams);
} } while (0)
1916 sac->sac_inbound_streams, sac->sac_outbound_streams))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Association change: streams (in/out) = (%u/%u)"
, sac->sac_inbound_streams, sac->sac_outbound_streams);
} } while (0)
;
1917
1918 if (NS_WARN_IF(!sac)NS_warn_if_impl(!sac, "!sac", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 1918)
) {
1919 return;
1920 }
1921
1922 n = sac->sac_length - sizeof(*sac);
1923 if ((sac->sac_state == SCTP_COMM_UP0x0001) || (sac->sac_state == SCTP_RESTART0x0003)) {
1924 if (n > 0) {
1925 for (i = 0; i < n; ++i) {
1926 switch (sac->sac_info[i]) {
1927 case SCTP_ASSOC_SUPPORTS_PR0x01:
1928 DC_DEBUG(("Supports: PR"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Supports: PR"); }
} while (0)
;
1929 break;
1930 case SCTP_ASSOC_SUPPORTS_AUTH0x02:
1931 DC_DEBUG(("Supports: AUTH"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Supports: AUTH")
; } } while (0)
;
1932 break;
1933 case SCTP_ASSOC_SUPPORTS_ASCONF0x03:
1934 DC_DEBUG(("Supports: ASCONF"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Supports: ASCONF"
); } } while (0)
;
1935 break;
1936 case SCTP_ASSOC_SUPPORTS_MULTIBUF0x04:
1937 DC_DEBUG(("Supports: MULTIBUF"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Supports: MULTIBUF"
); } } while (0)
;
1938 break;
1939 case SCTP_ASSOC_SUPPORTS_RE_CONFIG0x05:
1940 DC_DEBUG(("Supports: RE-CONFIG"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Supports: RE-CONFIG"
); } } while (0)
;
1941 break;
1942#if defined(SCTP_ASSOC_SUPPORTS_INTERLEAVING0x06)
1943 case SCTP_ASSOC_SUPPORTS_INTERLEAVING0x06:
1944 DC_DEBUG(("Supports: NDATA"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Supports: NDATA"
); } } while (0)
;
1945 // TODO: This should probably be set earlier above in 'case
1946 // SCTP_COMM_UP' but we also need this for 'SCTP_RESTART'.
1947 mSendInterleaved = true;
1948 break;
1949#endif
1950 default:
1951 DC_ERROR(("Supports: UNKNOWN(0x%02x)", sac->sac_info[i]))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "Supports: UNKNOWN(0x%02x)"
, sac->sac_info[i]); } } while (0)
;
1952 break;
1953 }
1954 }
1955 }
1956 } else if (((sac->sac_state == SCTP_COMM_LOST0x0002) ||
1957 (sac->sac_state == SCTP_CANT_STR_ASSOC0x0005)) &&
1958 (n > 0)) {
1959 DC_DEBUG(("Association: ABORT ="))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Association: ABORT ="
); } } while (0)
;
1960 for (i = 0; i < n; ++i) {
1961 DC_DEBUG((" 0x%02x", sac->sac_info[i]))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " 0x%02x", sac->
sac_info[i]); } } while (0)
;
1962 }
1963 }
1964 if ((sac->sac_state == SCTP_CANT_STR_ASSOC0x0005) ||
1965 (sac->sac_state == SCTP_SHUTDOWN_COMP0x0004) ||
1966 (sac->sac_state == SCTP_COMM_LOST0x0002)) {
1967 return;
1968 }
1969}
1970
1971void DataChannelConnection::HandlePeerAddressChangeEvent(
1972 const struct sctp_paddr_change* spc) {
1973 const char* addr = "";
1974#if !defined(__Userspace_os_Windows)
1975 char addr_buf[INET6_ADDRSTRLEN46];
1976 struct sockaddr_in* sin;
1977 struct sockaddr_in6* sin6;
1978#endif
1979
1980 switch (spc->spc_aaddr.ss_family) {
1981 case AF_INET2:
1982#if !defined(__Userspace_os_Windows)
1983 sin = (struct sockaddr_in*)&spc->spc_aaddr;
1984 addr = inet_ntop(AF_INET2, &sin->sin_addr, addr_buf, INET6_ADDRSTRLEN46);
1985#endif
1986 break;
1987 case AF_INET610:
1988#if !defined(__Userspace_os_Windows)
1989 sin6 = (struct sockaddr_in6*)&spc->spc_aaddr;
1990 addr = inet_ntop(AF_INET610, &sin6->sin6_addr, addr_buf, INET6_ADDRSTRLEN46);
1991#endif
1992 break;
1993 case AF_CONN123:
1994 addr = "DTLS connection";
1995 break;
1996 default:
1997 break;
1998 }
1999 DC_DEBUG(("Peer address %s is now ", addr))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Peer address %s is now "
, addr); } } while (0)
;
2000 switch (spc->spc_state) {
2001 case SCTP_ADDR_AVAILABLE0x0001:
2002 DC_DEBUG(("SCTP_ADDR_AVAILABLE"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "SCTP_ADDR_AVAILABLE"
); } } while (0)
;
2003 break;
2004 case SCTP_ADDR_UNREACHABLE0x0002:
2005 DC_DEBUG(("SCTP_ADDR_UNREACHABLE"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "SCTP_ADDR_UNREACHABLE"
); } } while (0)
;
2006 break;
2007 case SCTP_ADDR_REMOVED0x0003:
2008 DC_DEBUG(("SCTP_ADDR_REMOVED"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "SCTP_ADDR_REMOVED"
); } } while (0)
;
2009 break;
2010 case SCTP_ADDR_ADDED0x0004:
2011 DC_DEBUG(("SCTP_ADDR_ADDED"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "SCTP_ADDR_ADDED"
); } } while (0)
;
2012 break;
2013 case SCTP_ADDR_MADE_PRIM0x0005:
2014 DC_DEBUG(("SCTP_ADDR_MADE_PRIM"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "SCTP_ADDR_MADE_PRIM"
); } } while (0)
;
2015 break;
2016 case SCTP_ADDR_CONFIRMED0x0006:
2017 DC_DEBUG(("SCTP_ADDR_CONFIRMED"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "SCTP_ADDR_CONFIRMED"
); } } while (0)
;
2018 break;
2019 default:
2020 DC_ERROR(("UNKNOWN SCP STATE"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "UNKNOWN SCP STATE"
); } } while (0)
;
2021 break;
2022 }
2023 if (spc->spc_error) {
2024 DC_ERROR((" (error = 0x%08x).\n", spc->spc_error))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, " (error = 0x%08x).\n"
, spc->spc_error); } } while (0)
;
2025 }
2026}
2027
2028void DataChannelConnection::HandleRemoteErrorEvent(
2029 const struct sctp_remote_error* sre) {
2030 size_t i, n;
2031
2032 n = sre->sre_length - sizeof(struct sctp_remote_error);
2033 DC_WARN(("Remote Error (error = 0x%04x): ", sre->sre_error))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Warning)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Warning, "Remote Error (error = 0x%04x): "
, sre->sre_error); } } while (0)
;
2034 for (i = 0; i < n; ++i) {
2035 DC_WARN((" 0x%02x", sre->sre_data[i]))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Warning)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Warning, " 0x%02x", sre->
sre_data[i]); } } while (0)
;
2036 }
2037}
2038
2039void DataChannelConnection::HandleShutdownEvent(
2040 const struct sctp_shutdown_event* sse) {
2041 DC_DEBUG(("Shutdown event."))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Shutdown event."
); } } while (0)
;
2042 /* XXX: notify all channels. */
2043 // Attempts to actually send anything will fail
2044}
2045
2046void DataChannelConnection::HandleAdaptationIndication(
2047 const struct sctp_adaptation_event* sai) {
2048 DC_DEBUG(("Adaptation indication: %x.", sai->sai_adaptation_ind))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Adaptation indication: %x."
, sai->sai_adaptation_ind); } } while (0)
;
2049}
2050
2051void DataChannelConnection::HandlePartialDeliveryEvent(
2052 const struct sctp_pdapi_event* spde) {
2053 // Note: Be aware that stream and sequence number being u32 instead of u16 is
2054 // a bug in the SCTP API. This may change in the future.
2055
2056 DC_DEBUG(("Partial delivery event: "))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Partial delivery event: "
); } } while (0)
;
2057 switch (spde->pdapi_indication) {
2058 case SCTP_PARTIAL_DELIVERY_ABORTED0x0001:
2059 DC_DEBUG(("delivery aborted "))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "delivery aborted "
); } } while (0)
;
2060 break;
2061 default:
2062 DC_ERROR(("??? "))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "??? "); } } while
(0)
;
2063 break;
2064 }
2065 DC_DEBUG(("(flags = %x), stream = %" PRIu32 ", sn = %" PRIu32,do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "(flags = %x), stream = %"
"u" ", sn = %" "u", spde->pdapi_flags, spde->pdapi_stream
, spde->pdapi_seq); } } while (0)
2066 spde->pdapi_flags, spde->pdapi_stream, spde->pdapi_seq))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "(flags = %x), stream = %"
"u" ", sn = %" "u", spde->pdapi_flags, spde->pdapi_stream
, spde->pdapi_seq); } } while (0)
;
2067
2068 // Validate stream ID
2069 if (spde->pdapi_stream >= UINT16_MAX(65535)) {
2070 DC_ERROR(("Invalid stream id in partial delivery event: %" PRIu32 "\n",do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "Invalid stream id in partial delivery event: %"
"u" "\n", spde->pdapi_stream); } } while (0)
2071 spde->pdapi_stream))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "Invalid stream id in partial delivery event: %"
"u" "\n", spde->pdapi_stream); } } while (0)
;
2072 return;
2073 }
2074
2075 // Find channel and reset buffer
2076 DataChannel* channel = FindChannelByStream((uint16_t)spde->pdapi_stream);
2077 if (channel) {
2078 DC_WARN(("Abort partially delivered message of %zu bytes\n",do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Warning)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Warning, "Abort partially delivered message of %zu bytes\n"
, channel->mRecvBuffer.Length()); } } while (0)
2079 channel->mRecvBuffer.Length()))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Warning)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Warning, "Abort partially delivered message of %zu bytes\n"
, channel->mRecvBuffer.Length()); } } while (0)
;
2080 channel->mRecvBuffer.Truncate(0);
2081 }
2082}
2083
2084void DataChannelConnection::HandleSendFailedEvent(
2085 const struct sctp_send_failed_event* ssfe) {
2086 size_t i, n;
2087
2088 if (ssfe->ssfe_flags & SCTP_DATA_UNSENT0x0001) {
2089 DC_DEBUG(("Unsent "))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Unsent "); } } while
(0)
;
2090 }
2091 if (ssfe->ssfe_flags & SCTP_DATA_SENT0x0002) {
2092 DC_DEBUG(("Sent "))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Sent "); } } while
(0)
;
2093 }
2094 if (ssfe->ssfe_flags & ~(SCTP_DATA_SENT0x0002 | SCTP_DATA_UNSENT0x0001)) {
2095 DC_DEBUG(("(flags = %x) ", ssfe->ssfe_flags))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "(flags = %x) ", ssfe
->ssfe_flags); } } while (0)
;
2096 }
2097#ifdef XP_WIN
2098# define PRIPPID "lu"
2099#else
2100# define PRIPPID "u"
2101#endif
2102 DC_DEBUG(("message with PPID = %" PRIPPIDdo { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "message with PPID = %"
PRIPPID ", SID = %d, flags: 0x%04x due to error = 0x%08x", __bswap_32
(ssfe->ssfe_info.snd_ppid), ssfe->ssfe_info.snd_sid, ssfe
->ssfe_info.snd_flags, ssfe->ssfe_error); } } while (0)
2103 ", SID = %d, flags: 0x%04x due to error = 0x%08x",do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "message with PPID = %"
PRIPPID ", SID = %d, flags: 0x%04x due to error = 0x%08x", __bswap_32
(ssfe->ssfe_info.snd_ppid), ssfe->ssfe_info.snd_sid, ssfe
->ssfe_info.snd_flags, ssfe->ssfe_error); } } while (0)
2104 ntohl(ssfe->ssfe_info.snd_ppid), ssfe->ssfe_info.snd_sid,do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "message with PPID = %"
PRIPPID ", SID = %d, flags: 0x%04x due to error = 0x%08x", __bswap_32
(ssfe->ssfe_info.snd_ppid), ssfe->ssfe_info.snd_sid, ssfe
->ssfe_info.snd_flags, ssfe->ssfe_error); } } while (0)
2105 ssfe->ssfe_info.snd_flags, ssfe->ssfe_error))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "message with PPID = %"
PRIPPID ", SID = %d, flags: 0x%04x due to error = 0x%08x", __bswap_32
(ssfe->ssfe_info.snd_ppid), ssfe->ssfe_info.snd_sid, ssfe
->ssfe_info.snd_flags, ssfe->ssfe_error); } } while (0)
;
2106#undef PRIPPID
2107 n = ssfe->ssfe_length - sizeof(struct sctp_send_failed_event);
2108 for (i = 0; i < n; ++i) {
2109 DC_DEBUG((" 0x%02x", ssfe->ssfe_data[i]))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, " 0x%02x", ssfe->
ssfe_data[i]); } } while (0)
;
2110 }
2111}
2112
2113void DataChannelConnection::ClearResets() {
2114 // Clear all pending resets
2115 if (!mStreamsResetting.IsEmpty()) {
2116 DC_DEBUG(("Clearing resets for %zu streams", mStreamsResetting.Length()))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Clearing resets for %zu streams"
, mStreamsResetting.Length()); } } while (0)
;
2117 }
2118
2119 for (uint32_t i = 0; i < mStreamsResetting.Length(); ++i) {
2120 RefPtr<DataChannel> channel;
2121 channel = FindChannelByStream(mStreamsResetting[i]);
2122 if (channel) {
2123 DC_DEBUG(("Forgetting channel %u (%p) with pending reset",do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Forgetting channel %u (%p) with pending reset"
, channel->mStream, channel.get()); } } while (0)
2124 channel->mStream, channel.get()))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Forgetting channel %u (%p) with pending reset"
, channel->mStream, channel.get()); } } while (0)
;
2125 // TODO: Do we _really_ want to remove this? Are we allowed to reuse the
2126 // id?
2127 mChannels.Remove(channel);
2128 }
2129 }
2130 mStreamsResetting.Clear();
2131}
2132
2133void DataChannelConnection::ResetOutgoingStream(DataChannel& aChannel) {
2134 uint32_t i;
2135
2136 mLock.AssertCurrentThreadOwns();
2137 DC_DEBUG(("Connection %p: Resetting outgoing stream %u", (void*)this,do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Connection %p: Resetting outgoing stream %u"
, (void*)this, aChannel.mStream); } } while (0)
2138 aChannel.mStream))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Connection %p: Resetting outgoing stream %u"
, (void*)this, aChannel.mStream); } } while (0)
;
2139 aChannel.SetHasSentStreamReset();
2140 // Rarely has more than a couple items and only for a short time
2141 for (i = 0; i < mStreamsResetting.Length(); ++i) {
2142 if (mStreamsResetting[i] == aChannel.mStream) {
2143 return;
2144 }
2145 }
2146 mStreamsResetting.AppendElement(aChannel.mStream);
2147}
2148
2149void DataChannelConnection::SendOutgoingStreamReset() {
2150 struct sctp_reset_streams* srs;
2151 uint32_t i;
2152 size_t len;
2153
2154 DC_DEBUG(("Connection %p: Sending outgoing stream reset for %zu streams",do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Connection %p: Sending outgoing stream reset for %zu streams"
, (void*)this, mStreamsResetting.Length()); } } while (0)
2155 (void*)this, mStreamsResetting.Length()))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Connection %p: Sending outgoing stream reset for %zu streams"
, (void*)this, mStreamsResetting.Length()); } } while (0)
;
2156 mLock.AssertCurrentThreadOwns();
2157 if (mStreamsResetting.IsEmpty()) {
2158 DC_DEBUG(("No streams to reset"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "No streams to reset"
); } } while (0)
;
2159 return;
2160 }
2161 len = sizeof(sctp_assoc_t) +
2162 (2 + mStreamsResetting.Length()) * sizeof(uint16_t);
2163 srs = static_cast<struct sctp_reset_streams*>(
2164 moz_xmalloc(len)); // infallible malloc
2165 memset(srs, 0, len);
2166 srs->srs_flags = SCTP_STREAM_RESET_OUTGOING0x00000002;
2167 srs->srs_number_streams = mStreamsResetting.Length();
2168 for (i = 0; i < mStreamsResetting.Length(); ++i) {
2169 srs->srs_stream_list[i] = mStreamsResetting[i];
2170 }
2171 if (usrsctp_setsockopt(mMasterSocket, IPPROTO_SCTPIPPROTO_SCTP, SCTP_RESET_STREAMS0x00000901, srs,
2172 (socklen_t)len) < 0) {
2173 DC_ERROR(("***failed: setsockopt RESET, errno %d", errno))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "***failed: setsockopt RESET, errno %d"
, (*__errno_location ())); } } while (0)
;
2174 // if errno == EALREADY, this is normal - we can't send another reset
2175 // with one pending.
2176 // When we get an incoming reset (which may be a response to our
2177 // outstanding one), see if we have any pending outgoing resets and
2178 // send them
2179 } else {
2180 mStreamsResetting.Clear();
2181 }
2182 free(srs);
2183}
2184
2185void DataChannelConnection::HandleStreamResetEvent(
2186 const struct sctp_stream_reset_event* strrst) {
2187 uint32_t n, i;
2188 RefPtr<DataChannel> channel; // since we may null out the ref to the channel
2189
2190 if (!(strrst->strreset_flags & SCTP_STREAM_RESET_DENIED0x0004) &&
2191 !(strrst->strreset_flags & SCTP_STREAM_RESET_FAILED0x0008)) {
2192 n = (strrst->strreset_length - sizeof(struct sctp_stream_reset_event)) /
2193 sizeof(uint16_t);
2194 for (i = 0; i < n; ++i) {
2195 if (strrst->strreset_flags & SCTP_STREAM_RESET_INCOMING_SSN0x0001) {
2196 channel = FindChannelByStream(strrst->strreset_stream_list[i]);
2197 if (channel) {
2198 // The other side closed the channel
2199 // We could be in three states:
2200 // 1. Normal state (input and output streams (OPEN)
2201 // Notify application, send a RESET in response on our
2202 // outbound channel. Go to CLOSED
2203 // 2. We sent our own reset (CLOSING); either they crossed on the
2204 // wire, or this is a response to our Reset.
2205 // Go to CLOSED
2206 // 3. We've sent a open but haven't gotten a response yet (CONNECTING)
2207 // I believe this is impossible, as we don't have an input stream
2208 // yet.
2209
2210 DC_DEBUG(("Connection %p: stream %u closed", this, channel->mStream))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Connection %p: stream %u closed"
, this, channel->mStream); } } while (0)
;
2211 if (mChannels.Remove(channel) && !channel->HasSentStreamReset()) {
2212 // If we haven't already started closing this channel, mark the
2213 // stream for reset (the reset is sent below)
2214 ResetOutgoingStream(*channel);
2215 }
2216
2217 DC_DEBUG(("Disconnected DataChannel %p from connection %p",do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Disconnected DataChannel %p from connection %p"
, (void*)channel.get(), (void*)channel->mConnection.get())
; } } while (0)
2218 (void*)channel.get(), (void*)channel->mConnection.get()))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Disconnected DataChannel %p from connection %p"
, (void*)channel.get(), (void*)channel->mConnection.get())
; } } while (0)
;
2219 channel->StreamClosedLocked();
2220 } else {
2221 DC_WARN(("Connection %p: Can't find incoming stream %d", this,do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Warning)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Warning, "Connection %p: Can't find incoming stream %d"
, this, strrst->strreset_stream_list[i]); } } while (0)
2222 strrst->strreset_stream_list[i]))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Warning)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Warning, "Connection %p: Can't find incoming stream %d"
, this, strrst->strreset_stream_list[i]); } } while (0)
;
2223 }
2224 }
2225 }
2226 }
2227
2228 // Process any pending resets now:
2229 if (!mStreamsResetting.IsEmpty()) {
2230 DC_DEBUG(("Sending %zu pending resets", mStreamsResetting.Length()))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Sending %zu pending resets"
, mStreamsResetting.Length()); } } while (0)
;
2231 SendOutgoingStreamReset();
2232 }
2233}
2234
2235void DataChannelConnection::HandleStreamChangeEvent(
2236 const struct sctp_stream_change_event* strchg) {
2237 ASSERT_WEBRTC(!NS_IsMainThread())do { static_assert( mozilla::detail::AssertionConditionType<
decltype((!NS_IsMainThread()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((!NS_IsMainThread())))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("(!NS_IsMainThread())"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 2237); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(!NS_IsMainThread())"
")"); do { *((volatile int*)__null) = 2237; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2238 if (strchg->strchange_flags == SCTP_STREAM_CHANGE_DENIED0x0004) {
2239 DC_ERROR(("*** Failed increasing number of streams from %zu (%u/%u)",do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "*** Failed increasing number of streams from %zu (%u/%u)"
, mNegotiatedIdLimit, strchg->strchange_instrms, strchg->
strchange_outstrms); } } while (0)
2240 mNegotiatedIdLimit, strchg->strchange_instrms,do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "*** Failed increasing number of streams from %zu (%u/%u)"
, mNegotiatedIdLimit, strchg->strchange_instrms, strchg->
strchange_outstrms); } } while (0)
2241 strchg->strchange_outstrms))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "*** Failed increasing number of streams from %zu (%u/%u)"
, mNegotiatedIdLimit, strchg->strchange_instrms, strchg->
strchange_outstrms); } } while (0)
;
2242 // XXX FIX! notify pending opens of failure
2243 return;
2244 }
2245 if (strchg->strchange_instrms > mNegotiatedIdLimit) {
2246 DC_DEBUG(("Other side increased streams from %zu to %u", mNegotiatedIdLimit,do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Other side increased streams from %zu to %u"
, mNegotiatedIdLimit, strchg->strchange_instrms); } } while
(0)
2247 strchg->strchange_instrms))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Other side increased streams from %zu to %u"
, mNegotiatedIdLimit, strchg->strchange_instrms); } } while
(0)
;
2248 }
2249 uint16_t old_limit = mNegotiatedIdLimit;
2250 uint16_t new_limit =
2251 std::max(strchg->strchange_outstrms, strchg->strchange_instrms);
2252 if (new_limit > mNegotiatedIdLimit) {
2253 DC_DEBUG(("Increasing number of streams from %u to %u - adding %u (in: %u)",do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Increasing number of streams from %u to %u - adding %u (in: %u)"
, old_limit, new_limit, new_limit - old_limit, strchg->strchange_instrms
); } } while (0)
2254 old_limit, new_limit, new_limit - old_limit,do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Increasing number of streams from %u to %u - adding %u (in: %u)"
, old_limit, new_limit, new_limit - old_limit, strchg->strchange_instrms
); } } while (0)
2255 strchg->strchange_instrms))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Increasing number of streams from %u to %u - adding %u (in: %u)"
, old_limit, new_limit, new_limit - old_limit, strchg->strchange_instrms
); } } while (0)
;
2256 // make sure both are the same length
2257 mNegotiatedIdLimit = new_limit;
2258 DC_DEBUG(("New length = %zu (was %d)", mNegotiatedIdLimit, old_limit))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "New length = %zu (was %d)"
, mNegotiatedIdLimit, old_limit); } } while (0)
;
2259 // Re-process any channels waiting for streams.
2260 // Linear search, but we don't increase channels often and
2261 // the array would only get long in case of an app error normally
2262
2263 // Make sure we request enough streams if there's a big jump in streams
2264 // Could make a more complex API for OpenXxxFinish() and avoid this loop
2265 auto channels = mChannels.GetAll();
2266 size_t num_needed =
2267 channels.Length() ? (channels.LastElement()->mStream + 1) : 0;
2268 MOZ_ASSERT(num_needed != INVALID_STREAM)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(num_needed != (0xFFFF))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(num_needed != (0xFFFF)))), 0
))) { do { } while (false); MOZ_ReportAssertionFailure("num_needed != (0xFFFF)"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 2268); AnnotateMozCrashReason("MOZ_ASSERT" "(" "num_needed != (0xFFFF)"
")"); do { *((volatile int*)__null) = 2268; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2269 if (num_needed > new_limit) {
2270 int32_t more_needed = num_needed - ((int32_t)mNegotiatedIdLimit) + 16;
2271 DC_DEBUG(("Not enough new streams, asking for %d more", more_needed))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Not enough new streams, asking for %d more"
, more_needed); } } while (0)
;
2272 // TODO: parameter is an int32_t but we pass size_t
2273 RequestMoreStreams(more_needed);
2274 } else if (strchg->strchange_outstrms < strchg->strchange_instrms) {
2275 DC_DEBUG(("Requesting %d output streams to match partner",do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Requesting %d output streams to match partner"
, strchg->strchange_instrms - strchg->strchange_outstrms
); } } while (0)
2276 strchg->strchange_instrms - strchg->strchange_outstrms))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Requesting %d output streams to match partner"
, strchg->strchange_instrms - strchg->strchange_outstrms
); } } while (0)
;
2277 RequestMoreStreams(strchg->strchange_instrms -
2278 strchg->strchange_outstrms);
2279 }
2280
2281 ProcessQueuedOpens();
2282 }
2283 // else probably not a change in # of streams
2284
2285 if ((strchg->strchange_flags & SCTP_STREAM_CHANGE_DENIED0x0004) ||
2286 (strchg->strchange_flags & SCTP_STREAM_CHANGE_FAILED0x0008)) {
2287 // Other side denied our request. Need to AnnounceClosed some stuff.
2288 for (auto& channel : mChannels.GetAll()) {
2289 if (channel->mStream >= mNegotiatedIdLimit) {
2290 /* XXX: Signal to the other end. */
2291 channel->AnnounceClosed();
2292 // maybe fire onError (bug 843625)
2293 }
2294 }
2295 }
2296}
2297
2298void DataChannelConnection::HandleNotification(
2299 const union sctp_notification* notif, size_t n) {
2300 mLock.AssertCurrentThreadOwns();
2301 if (notif->sn_header.sn_length != (uint32_t)n) {
2302 return;
2303 }
2304 switch (notif->sn_header.sn_type) {
2305 case SCTP_ASSOC_CHANGE0x0001:
2306 HandleAssociationChangeEvent(&(notif->sn_assoc_change));
2307 break;
2308 case SCTP_PEER_ADDR_CHANGE0x0002:
2309 HandlePeerAddressChangeEvent(&(notif->sn_paddr_change));
2310 break;
2311 case SCTP_REMOTE_ERROR0x0003:
2312 HandleRemoteErrorEvent(&(notif->sn_remote_error));
2313 break;
2314 case SCTP_SHUTDOWN_EVENT0x0005:
2315 HandleShutdownEvent(&(notif->sn_shutdown_event));
2316 break;
2317 case SCTP_ADAPTATION_INDICATION0x0006:
2318 HandleAdaptationIndication(&(notif->sn_adaptation_event));
2319 break;
2320 case SCTP_AUTHENTICATION_EVENT0x0008:
2321 DC_DEBUG(("SCTP_AUTHENTICATION_EVENT"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "SCTP_AUTHENTICATION_EVENT"
); } } while (0)
;
2322 break;
2323 case SCTP_SENDER_DRY_EVENT0x000a:
2324 // DC_DEBUG(("SCTP_SENDER_DRY_EVENT"));
2325 break;
2326 case SCTP_NOTIFICATIONS_STOPPED_EVENT0x000b:
2327 DC_DEBUG(("SCTP_NOTIFICATIONS_STOPPED_EVENT"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "SCTP_NOTIFICATIONS_STOPPED_EVENT"
); } } while (0)
;
2328 break;
2329 case SCTP_PARTIAL_DELIVERY_EVENT0x0007:
2330 HandlePartialDeliveryEvent(&(notif->sn_pdapi_event));
2331 break;
2332 case SCTP_SEND_FAILED_EVENT0x000e:
2333 HandleSendFailedEvent(&(notif->sn_send_failed_event));
2334 break;
2335 case SCTP_STREAM_RESET_EVENT0x0009:
2336 HandleStreamResetEvent(&(notif->sn_strreset_event));
2337 break;
2338 case SCTP_ASSOC_RESET_EVENT0x000c:
2339 DC_DEBUG(("SCTP_ASSOC_RESET_EVENT"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "SCTP_ASSOC_RESET_EVENT"
); } } while (0)
;
2340 break;
2341 case SCTP_STREAM_CHANGE_EVENT0x000d:
2342 HandleStreamChangeEvent(&(notif->sn_strchange_event));
2343 break;
2344 default:
2345 DC_ERROR(("unknown SCTP event: %u", (uint32_t)notif->sn_header.sn_type))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "unknown SCTP event: %u"
, (uint32_t)notif->sn_header.sn_type); } } while (0)
;
2346 break;
2347 }
2348}
2349
2350int DataChannelConnection::ReceiveCallback(
2351 struct socket* sock, void* data, size_t datalen, struct sctp_rcvinfo rcv,
2352 int flags) MOZ_NO_THREAD_SAFETY_ANALYSIS__attribute__((no_thread_safety_analysis)) {
2353 ASSERT_WEBRTC(!NS_IsMainThread())do { static_assert( mozilla::detail::AssertionConditionType<
decltype((!NS_IsMainThread()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((!NS_IsMainThread())))), 0))
) { do { } while (false); MOZ_ReportAssertionFailure("(!NS_IsMainThread())"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 2353); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(!NS_IsMainThread())"
")"); do { *((volatile int*)__null) = 2353; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2354 DC_DEBUG(("In ReceiveCallback"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "In ReceiveCallback"
); } } while (0)
;
2355
2356 // libusrsctp just went reentrant on us. Put a stop to this.
2357 mSTS->Dispatch(NS_NewRunnableFunction(
2358 "DataChannelConnection::ReceiveCallback",
2359 [data, datalen, rcv, flags, this,
2360 self = RefPtr<DataChannelConnection>(this)]() mutable {
2361 if (!data) {
2362 DC_DEBUG(("ReceiveCallback: SCTP has finished shutting down"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "ReceiveCallback: SCTP has finished shutting down"
); } } while (0)
;
2363 } else {
2364 mLock.Lock();
2365 if (flags & MSG_NOTIFICATION0x2000) {
2366 HandleNotification(static_cast<union sctp_notification*>(data),
2367 datalen);
2368 } else {
2369 HandleMessage(data, datalen, ntohl(rcv.rcv_ppid)__bswap_32 (rcv.rcv_ppid), rcv.rcv_sid,
2370 flags);
2371 }
2372 mLock.Unlock();
2373 // sctp allocates 'data' with malloc(), and expects the receiver to
2374 // free it (presumably with free).
2375 // XXX future optimization: try to deliver messages without an
2376 // internal alloc/copy, and if so delay the free until later.
2377 free(data);
2378 }
2379 }));
2380
2381 // usrsctp defines the callback as returning an int, but doesn't use it
2382 return 1;
2383}
2384
2385already_AddRefed<DataChannel> DataChannelConnection::Open(
2386 const nsACString& label, const nsACString& protocol,
2387 DataChannelReliabilityPolicy prPolicy, bool inOrder, uint32_t prValue,
2388 DataChannelListener* aListener, nsISupports* aContext,
2389 bool aExternalNegotiated, uint16_t aStream) {
2390 ASSERT_WEBRTC(NS_IsMainThread())do { static_assert( mozilla::detail::AssertionConditionType<
decltype((NS_IsMainThread()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((NS_IsMainThread())))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("(NS_IsMainThread())"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 2390); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(NS_IsMainThread())"
")"); do { *((volatile int*)__null) = 2390; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2391 MutexAutoLock lock(mLock); // OpenFinish assumes this
2392 if (!aExternalNegotiated) {
2393 if (mAllocateEven.isSome()) {
2394 aStream = FindFreeStream();
2395 if (aStream == INVALID_STREAM(0xFFFF)) {
2396 return nullptr;
2397 }
2398 } else {
2399 // We do not yet know whether we are client or server, and an id has not
2400 // been chosen for us. We will need to choose later.
2401 aStream = INVALID_STREAM(0xFFFF);
2402 }
2403 }
2404
2405 DC_DEBUG(do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "DC Open: label %s/%s, type %s, inorder %d, prValue %u, listener %p, "
"context %p, external: %s, stream %u", TPromiseFlatString<
char>(label).get(), TPromiseFlatString<char>(protocol
).get(), ToString(prPolicy), inOrder, prValue, aListener, aContext
, aExternalNegotiated ? "true" : "false", aStream); } } while
(0)
2406 ("DC Open: label %s/%s, type %s, inorder %d, prValue %u, listener %p, "do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "DC Open: label %s/%s, type %s, inorder %d, prValue %u, listener %p, "
"context %p, external: %s, stream %u", TPromiseFlatString<
char>(label).get(), TPromiseFlatString<char>(protocol
).get(), ToString(prPolicy), inOrder, prValue, aListener, aContext
, aExternalNegotiated ? "true" : "false", aStream); } } while
(0)
2407 "context %p, external: %s, stream %u",do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "DC Open: label %s/%s, type %s, inorder %d, prValue %u, listener %p, "
"context %p, external: %s, stream %u", TPromiseFlatString<
char>(label).get(), TPromiseFlatString<char>(protocol
).get(), ToString(prPolicy), inOrder, prValue, aListener, aContext
, aExternalNegotiated ? "true" : "false", aStream); } } while
(0)
2408 PromiseFlatCString(label).get(), PromiseFlatCString(protocol).get(),do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "DC Open: label %s/%s, type %s, inorder %d, prValue %u, listener %p, "
"context %p, external: %s, stream %u", TPromiseFlatString<
char>(label).get(), TPromiseFlatString<char>(protocol
).get(), ToString(prPolicy), inOrder, prValue, aListener, aContext
, aExternalNegotiated ? "true" : "false", aStream); } } while
(0)
2409 ToString(prPolicy), inOrder, prValue, aListener, aContext,do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "DC Open: label %s/%s, type %s, inorder %d, prValue %u, listener %p, "
"context %p, external: %s, stream %u", TPromiseFlatString<
char>(label).get(), TPromiseFlatString<char>(protocol
).get(), ToString(prPolicy), inOrder, prValue, aListener, aContext
, aExternalNegotiated ? "true" : "false", aStream); } } while
(0)
2410 aExternalNegotiated ? "true" : "false", aStream))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "DC Open: label %s/%s, type %s, inorder %d, prValue %u, listener %p, "
"context %p, external: %s, stream %u", TPromiseFlatString<
char>(label).get(), TPromiseFlatString<char>(protocol
).get(), ToString(prPolicy), inOrder, prValue, aListener, aContext
, aExternalNegotiated ? "true" : "false", aStream); } } while
(0)
;
2411
2412 if ((prPolicy == DataChannelReliabilityPolicy::Reliable) && (prValue != 0)) {
2413 return nullptr;
2414 }
2415
2416 if (aStream != INVALID_STREAM(0xFFFF) && mChannels.Get(aStream)) {
2417 DC_ERROR(("external negotiation of already-open channel %u", aStream))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "external negotiation of already-open channel %u"
, aStream); } } while (0)
;
2418 return nullptr;
2419 }
2420
2421 RefPtr<DataChannel> channel(new DataChannel(
2422 this, aStream, DataChannelState::Connecting, label, protocol, prPolicy,
2423 prValue, inOrder, aExternalNegotiated, aListener, aContext));
2424 mChannels.Insert(channel);
2425
2426 return OpenFinish(channel.forget());
2427}
2428
2429// Separate routine so we can also call it to finish up from pending opens
2430already_AddRefed<DataChannel> DataChannelConnection::OpenFinish(
2431 already_AddRefed<DataChannel>&& aChannel) {
2432 RefPtr<DataChannel> channel(aChannel); // takes the reference passed in
2433 // Normally 1 reference if called from ::Open(), or 2 if called from
2434 // ProcessQueuedOpens() unless the DOMDataChannel was gc'd
2435 const uint16_t stream = channel->mStream;
2436
2437 mLock.AssertCurrentThreadOwns();
2438
2439 // Cases we care about:
2440 // Pre-negotiated:
2441 // Not Open:
2442 // Doesn't fit:
2443 // -> change initial ask or renegotiate after open
2444 // -> queue open
2445 // Open:
2446 // Doesn't fit:
2447 // -> RequestMoreStreams && queue
2448 // Does fit:
2449 // -> open
2450 // Not negotiated:
2451 // Not Open:
2452 // -> queue open
2453 // Open:
2454 // -> Try to get a stream
2455 // Doesn't fit:
2456 // -> RequestMoreStreams && queue
2457 // Does fit:
2458 // -> open
2459 // So the Open cases are basically the same
2460 // Not Open cases are simply queue for non-negotiated, and
2461 // either change the initial ask or possibly renegotiate after open.
2462 DataChannelConnectionState state = GetState();
2463 if (state != DataChannelConnectionState::Open ||
24
Assuming 'state' is not equal to Open
2464 stream >= mNegotiatedIdLimit) {
2465 if (state
24.1
'state' is not equal to Open
== DataChannelConnectionState::Open) {
25
Taking false branch
2466 MOZ_ASSERT(stream != INVALID_STREAM)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(stream != (0xFFFF))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(stream != (0xFFFF)))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("stream != (0xFFFF)"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 2466); AnnotateMozCrashReason("MOZ_ASSERT" "(" "stream != (0xFFFF)"
")"); do { *((volatile int*)__null) = 2466; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2467 // RequestMoreStreams() limits to MAX_NUM_STREAMS -- allocate extra
2468 // streams to avoid going back immediately for more if the ask to N, N+1,
2469 // etc
2470 int32_t more_needed = stream - ((int32_t)mNegotiatedIdLimit) + 16;
2471 if (!RequestMoreStreams(more_needed)) {
2472 // Something bad happened... we're done
2473 goto request_error_cleanup;
2474 }
2475 }
2476 DC_DEBUG(("Queuing channel %p (%u) to finish open", channel.get(), stream))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Queuing channel %p (%u) to finish open"
, channel.get(), stream); } } while (0)
;
26
Assuming the condition is true
27
Taking false branch
28
Loop condition is false. Exiting loop
2477 // Also serves to mark we told the app
2478 channel->mHasFinishedOpen = true;
2479 mPending.insert(channel);
29
Method called on moved-from object 'mPending' of type 'std::set'
2480 return channel.forget();
2481 }
2482
2483 MOZ_ASSERT(stream != INVALID_STREAM)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(stream != (0xFFFF))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(stream != (0xFFFF)))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("stream != (0xFFFF)"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 2483); AnnotateMozCrashReason("MOZ_ASSERT" "(" "stream != (0xFFFF)"
")"); do { *((volatile int*)__null) = 2483; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2484 MOZ_ASSERT(stream < mNegotiatedIdLimit)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(stream < mNegotiatedIdLimit)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(stream < mNegotiatedIdLimit
))), 0))) { do { } while (false); MOZ_ReportAssertionFailure(
"stream < mNegotiatedIdLimit", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 2484); AnnotateMozCrashReason("MOZ_ASSERT" "(" "stream < mNegotiatedIdLimit"
")"); do { *((volatile int*)__null) = 2484; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2485
2486#ifdef TEST_QUEUED_DATA
2487 // It's painful to write a test for this...
2488 channel->AnnounceOpen();
2489 SendDataMsgInternalOrBuffer(channel, "Help me!", 8,
2490 DATA_CHANNEL_PPID_DOMSTRING51);
2491#endif
2492
2493 if (!channel->mNegotiated) {
2494 if (!channel->mOrdered) {
2495 // Don't send unordered until this gets cleared.
2496 channel->mWaitingForAck = true;
2497 }
2498
2499 int error = SendOpenRequestMessage(channel->mLabel, channel->mProtocol,
2500 stream, !channel->mOrdered,
2501 channel->mPrPolicy, channel->mPrValue);
2502 if (error) {
2503 DC_ERROR(("SendOpenRequest failed, error = %d", error))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "SendOpenRequest failed, error = %d"
, error); } } while (0)
;
2504 if (channel->mHasFinishedOpen) {
2505 // We already returned the channel to the app.
2506 NS_ERROR("Failed to send open request")do { NS_DebugBreak(NS_DEBUG_ASSERTION, "Failed to send open request"
, "Error", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 2506); MOZ_PretendNoReturn(); } while (0)
;
2507 channel->AnnounceClosed();
2508 }
2509 // If we haven't returned the channel yet, it will get destroyed when we
2510 // exit this function.
2511 mChannels.Remove(channel);
2512 // we'll be destroying the channel
2513 return nullptr;
2514 /* NOTREACHED */
2515 }
2516 }
2517
2518 // Either externally negotiated or we sent Open
2519 // FIX? Move into DOMDataChannel? I don't think we can send it yet here
2520 channel->AnnounceOpen();
2521
2522 return channel.forget();
2523
2524request_error_cleanup:
2525 if (channel->mHasFinishedOpen) {
2526 // We already returned the channel to the app.
2527 NS_ERROR("Failed to request more streams")do { NS_DebugBreak(NS_DEBUG_ASSERTION, "Failed to request more streams"
, "Error", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 2527); MOZ_PretendNoReturn(); } while (0)
;
2528 channel->AnnounceClosed();
2529 return channel.forget();
2530 }
2531 // we'll be destroying the channel, but it never really got set up
2532 // Alternative would be to RUN_ON_THREAD(channel.forget(),::Destroy,...) and
2533 // Dispatch it to ourselves
2534 return nullptr;
2535}
2536
2537// Returns a POSIX error code directly instead of setting errno.
2538int DataChannelConnection::SendMsgInternal(OutgoingMsg& msg, size_t* aWritten) {
2539 mLock.AssertCurrentThreadOwns();
2540 struct sctp_sndinfo& info = msg.GetInfo().sendv_sndinfo;
2541 int error;
2542
2543 // EOR set?
2544 bool eor_set = (info.snd_flags & SCTP_EOR0x2000) != 0;
2545
2546 // Send until buffer is empty
2547 Span<const uint8_t> toSend = msg.GetRemainingData();
2548 do {
2549 // Carefully chunk the buffer
2550 if (toSend.Length() > DATA_CHANNEL_MAX_BINARY_FRAGMENT0x4000) {
2551 toSend = toSend.To(DATA_CHANNEL_MAX_BINARY_FRAGMENT0x4000);
2552
2553 // Unset EOR flag
2554 info.snd_flags &= ~SCTP_EOR0x2000;
2555 } else {
2556 // Set EOR flag
2557 if (eor_set) {
2558 info.snd_flags |= SCTP_EOR0x2000;
2559 }
2560 }
2561
2562 // Send (or try at least)
2563 // SCTP will return EMSGSIZE if the message is bigger than the buffer
2564 // size (or EAGAIN if there isn't space). However, we can avoid EMSGSIZE
2565 // by carefully crafting small enough message chunks.
2566 ssize_t written = usrsctp_sendv(mSocket, toSend.Elements(), toSend.Length(),
2567 nullptr, 0, (void*)&msg.GetInfo(),
2568 (socklen_t)sizeof(struct sctp_sendv_spa),
2569 SCTP_SENDV_SPA4, 0);
2570
2571 if (written < 0) {
2572 error = errno(*__errno_location ());
2573 goto out;
2574 }
2575
2576 if (aWritten) {
2577 *aWritten += written;
2578 }
2579 DC_DEBUG(("Sent buffer (written=%zu, len=%zu, left=%zu)", (size_t)written,do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Sent buffer (written=%zu, len=%zu, left=%zu)"
, (size_t)written, toSend.Length(), msg.GetRemainingData().Length
() - (size_t)written); } } while (0)
2580 toSend.Length(),do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Sent buffer (written=%zu, len=%zu, left=%zu)"
, (size_t)written, toSend.Length(), msg.GetRemainingData().Length
() - (size_t)written); } } while (0)
2581 msg.GetRemainingData().Length() - (size_t)written))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Sent buffer (written=%zu, len=%zu, left=%zu)"
, (size_t)written, toSend.Length(), msg.GetRemainingData().Length
() - (size_t)written); } } while (0)
;
2582
2583 // TODO: Remove once resolved
2584 // (https://github.com/sctplab/usrsctp/issues/132)
2585 if (written == 0) {
2586 DC_ERROR(("@tuexen: usrsctp_sendv returned 0"))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "@tuexen: usrsctp_sendv returned 0"
); } } while (0)
;
2587 error = EAGAIN11;
2588 goto out;
2589 }
2590
2591 // If not all bytes have been written, this obviously means that usrsctp's
2592 // buffer is full and we need to try again later.
2593 if ((size_t)written < toSend.Length()) {
2594 msg.Advance((size_t)written);
2595 error = EAGAIN11;
2596 goto out;
2597 }
2598
2599 // Update buffer position
2600 msg.Advance((size_t)written);
2601
2602 // Get amount of bytes left in the buffer
2603 toSend = msg.GetRemainingData();
2604 } while (toSend.Length() > 0);
2605
2606 // Done
2607 error = 0;
2608
2609out:
2610 // Reset EOR flag
2611 if (eor_set) {
2612 info.snd_flags |= SCTP_EOR0x2000;
2613 }
2614
2615 return error;
2616}
2617
2618// Returns a POSIX error code directly instead of setting errno.
2619// IMPORTANT: Ensure that the buffer passed is guarded by mLock!
2620int DataChannelConnection::SendMsgInternalOrBuffer(
2621 nsTArray<UniquePtr<BufferedOutgoingMsg>>& buffer, OutgoingMsg& msg,
2622 bool& buffered, size_t* aWritten) {
2623 NS_WARNING_ASSERTION(msg.GetLength() > 0, "Length is 0?!")do { if (!(msg.GetLength() > 0)) { NS_DebugBreak(NS_DEBUG_WARNING
, "Length is 0?!", "msg.GetLength() > 0", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 2623); } } while (false)
;
2624
2625 int error = 0;
2626 bool need_buffering = false;
2627
2628 // Note: Main-thread IO, but doesn't block!
2629 // XXX FIX! to deal with heavy overruns of JS trying to pass data in
2630 // (more than the buffersize) queue data onto another thread to do the
2631 // actual sends. See netwerk/protocol/websocket/WebSocketChannel.cpp
2632
2633 // Avoid a race between buffer-full-failure (where we have to add the
2634 // packet to the buffered-data queue) and the buffer-now-only-half-full
2635 // callback, which happens on a different thread. Otherwise we might
2636 // fail here, then before we add it to the queue get the half-full
2637 // callback, find nothing to do, then on this thread add it to the
2638 // queue - which would sit there. Also, if we later send more data, it
2639 // would arrive ahead of the buffered message, but if the buffer ever
2640 // got to 1/2 full, the message would get sent - but at a semi-random
2641 // time, after other data it was supposed to be in front of.
2642
2643 // Must lock before empty check for similar reasons!
2644 mLock.AssertCurrentThreadOwns();
2645 if (buffer.IsEmpty() &&
2646 (mSendInterleaved || mPendingType == PendingType::None)) {
2647 error = SendMsgInternal(msg, aWritten);
2648 switch (error) {
2649 case 0:
2650 break;
2651 case EAGAIN11:
2652#if (EAGAIN11 != EWOULDBLOCK11)
2653 case EWOULDBLOCK11:
2654#endif
2655 need_buffering = true;
2656 break;
2657 default:
2658 DC_ERROR(("error %d on sending", error))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "error %d on sending"
, error); } } while (0)
;
2659 break;
2660 }
2661 } else {
2662 need_buffering = true;
2663 }
2664
2665 if (need_buffering) {
2666 // queue data for resend! And queue any further data for the stream until
2667 // it is...
2668 buffer.EmplaceBack(
2669 BufferedOutgoingMsg::CopyFrom(msg)); // owned by mBufferedData array
2670 DC_DEBUG(("Queued %zu buffers (left=%zu, total=%zu)", buffer.Length(),do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Queued %zu buffers (left=%zu, total=%zu)"
, buffer.Length(), buffer.LastElement()->GetLength(), msg.
GetLength()); } } while (0)
2671 buffer.LastElement()->GetLength(), msg.GetLength()))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Queued %zu buffers (left=%zu, total=%zu)"
, buffer.Length(), buffer.LastElement()->GetLength(), msg.
GetLength()); } } while (0)
;
2672 buffered = true;
2673 return 0;
2674 }
2675
2676 buffered = false;
2677 return error;
2678}
2679
2680// Caller must ensure that length <= UINT32_MAX
2681// Returns a POSIX error code.
2682int DataChannelConnection::SendDataMsgInternalOrBuffer(DataChannel& channel,
2683 const uint8_t* data,
2684 size_t len,
2685 uint32_t ppid) {
2686 // Note that `channel.mConnection` is `this`. This is just here to satisfy
2687 // the thread safety annotations on DataChannel.
2688 channel.mConnection->mLock.AssertCurrentThreadOwns();
2689
2690 if (NS_WARN_IF(channel.GetReadyState() != DataChannelState::Open)NS_warn_if_impl(channel.GetReadyState() != DataChannelState::
Open, "channel.GetReadyState() != DataChannelState::Open", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 2690)
) {
2691 return EINVAL22; // TODO: Find a better error code
2692 }
2693
2694 struct sctp_sendv_spa info = {};
2695
2696 // General flags
2697 info.sendv_flags = SCTP_SEND_SNDINFO_VALID0x00000001;
2698
2699 // Set stream identifier, protocol identifier and flags
2700 info.sendv_sndinfo.snd_sid = channel.mStream;
2701 info.sendv_sndinfo.snd_flags = SCTP_EOR0x2000;
2702 info.sendv_sndinfo.snd_ppid = htonl(ppid)__bswap_32 (ppid);
2703
2704 // Unordered?
2705 // To avoid problems where an in-order OPEN is lost and an
2706 // out-of-order data message "beats" it, require data to be in-order
2707 // until we get an ACK.
2708 if (!channel.mOrdered && !channel.mWaitingForAck) {
2709 info.sendv_sndinfo.snd_flags |= SCTP_UNORDERED0x0400;
2710 }
2711
2712 // Partial reliability policy
2713 if (channel.mPrPolicy != DataChannelReliabilityPolicy::Reliable) {
2714 info.sendv_prinfo.pr_policy = ToUsrsctpValue(channel.mPrPolicy);
2715 info.sendv_prinfo.pr_value = channel.mPrValue;
2716 info.sendv_flags |= SCTP_SEND_PRINFO_VALID0x00000002;
2717 }
2718
2719 // Create message instance and send
2720 OutgoingMsg msg(info, Span(data, len));
2721 bool buffered;
2722 size_t written = 0;
2723 mDeferSend = true;
2724 int error =
2725 SendMsgInternalOrBuffer(channel.mBufferedData, msg, buffered, &written);
2726 mDeferSend = false;
2727 if (written && ppid != DATA_CHANNEL_PPID_DOMSTRING_EMPTY56 &&
2728 ppid != DATA_CHANNEL_PPID_BINARY_EMPTY57) {
2729 channel.DecrementBufferedAmount(written);
2730 }
2731
2732 for (auto&& packet : mDeferredSend) {
2733 MOZ_ASSERT(written)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(written)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(written))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("written", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 2733); AnnotateMozCrashReason("MOZ_ASSERT" "(" "written" ")"
); do { *((volatile int*)__null) = 2733; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2734 SendPacket(std::move(packet));
2735 }
2736 mDeferredSend.clear();
2737
2738 // Set pending type and stream index (if buffered)
2739 if (!error && buffered && mPendingType == PendingType::None) {
2740 mPendingType = PendingType::Data;
2741 mCurrentStream = channel.mStream;
2742 }
2743 return error;
2744}
2745
2746// Caller must ensure that length <= UINT32_MAX
2747// Returns a POSIX error code.
2748int DataChannelConnection::SendDataMsg(DataChannel& channel,
2749 const uint8_t* data, size_t len,
2750 uint32_t ppidPartial,
2751 uint32_t ppidFinal) {
2752 // We *really* don't want to do this from main thread! - and
2753 // SendDataMsgInternalOrBuffer avoids blocking.
2754 mLock.AssertCurrentThreadOwns();
2755
2756 if (mMaxMessageSize != 0 && len > mMaxMessageSize) {
2757 DC_ERROR(("Message rejected, too large (%zu > %" PRIu64 ")", len,do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "Message rejected, too large (%zu > %"
"l" "u" ")", len, mMaxMessageSize); } } while (0)
2758 mMaxMessageSize))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "Message rejected, too large (%zu > %"
"l" "u" ")", len, mMaxMessageSize); } } while (0)
;
2759 return EMSGSIZE90;
2760 }
2761
2762 // This will use EOR-based fragmentation if the message is too large (> 64
2763 // KiB)
2764 return SendDataMsgInternalOrBuffer(channel, data, len, ppidFinal);
2765}
2766
2767class ReadBlobRunnable : public Runnable {
2768 public:
2769 ReadBlobRunnable(DataChannelConnection* aConnection, uint16_t aStream,
2770 nsIInputStream* aBlob)
2771 : Runnable("ReadBlobRunnable"),
2772 mConnection(aConnection),
2773 mStream(aStream),
2774 mBlob(aBlob) {}
2775
2776 NS_IMETHODvirtual nsresult Run() override {
2777 // ReadBlob() is responsible to releasing the reference
2778 DataChannelConnection* self = mConnection;
2779 self->ReadBlob(mConnection.forget(), mStream, mBlob);
2780 return NS_OK;
2781 }
2782
2783 private:
2784 // Make sure the Connection doesn't die while there are jobs outstanding.
2785 // Let it die (if released by PeerConnectionImpl while we're running)
2786 // when we send our runnable back to MainThread. Then ~DataChannelConnection
2787 // can send the IOThread to MainThread to die in a runnable, avoiding
2788 // unsafe event loop recursion. Evil.
2789 RefPtr<DataChannelConnection> mConnection;
2790 uint16_t mStream;
2791 // Use RefCount for preventing the object is deleted when SendBlob returns.
2792 RefPtr<nsIInputStream> mBlob;
2793};
2794
2795// Returns a POSIX error code.
2796int DataChannelConnection::SendBlob(uint16_t stream, nsIInputStream* aBlob) {
2797 MutexAutoLock lock(mLock);
2798 RefPtr<DataChannel> channel = mChannels.Get(stream);
2799 if (NS_WARN_IF(!channel)NS_warn_if_impl(!channel, "!channel", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 2799)
) {
2800 return EINVAL22; // TODO: Find a better error code
2801 }
2802
2803 // Spawn a thread to send the data
2804 if (!mInternalIOThread) {
2805 nsresult rv =
2806 NS_NewNamedThread("DataChannel IO", getter_AddRefs(mInternalIOThread));
2807 if (NS_FAILED(rv)((bool)(__builtin_expect(!!(NS_FAILED_impl(rv)), 0)))) {
2808 return EINVAL22; // TODO: Find a better error code
2809 }
2810 }
2811
2812 mInternalIOThread->Dispatch(
2813 do_AddRef(new ReadBlobRunnable(this, stream, aBlob)), NS_DISPATCH_NORMALnsIEventTarget::DISPATCH_NORMAL);
2814 return 0;
2815}
2816
2817class DataChannelBlobSendRunnable : public Runnable {
2818 public:
2819 DataChannelBlobSendRunnable(
2820 already_AddRefed<DataChannelConnection>& aConnection, uint16_t aStream)
2821 : Runnable("DataChannelBlobSendRunnable"),
2822 mConnection(aConnection),
2823 mStream(aStream) {}
2824
2825 ~DataChannelBlobSendRunnable() override {
2826 if (!NS_IsMainThread() && mConnection) {
2827 MOZ_ASSERT(false)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(false)>::isValid, "invalid assertion condition");
if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while (
false); MOZ_ReportAssertionFailure("false", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 2827); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ")")
; do { *((volatile int*)__null) = 2827; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2828 // explicitly leak the connection if destroyed off mainthread
2829 Unused << mConnection.forget().take();
2830 }
2831 }
2832
2833 NS_IMETHODvirtual nsresult Run() override {
2834 ASSERT_WEBRTC(NS_IsMainThread())do { static_assert( mozilla::detail::AssertionConditionType<
decltype((NS_IsMainThread()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((NS_IsMainThread())))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("(NS_IsMainThread())"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 2834); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(NS_IsMainThread())"
")"); do { *((volatile int*)__null) = 2834; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2835
2836 mConnection->SendBinaryMsg(mStream, mData);
2837 mConnection = nullptr;
2838 return NS_OK;
2839 }
2840
2841 // explicitly public so we can avoid allocating twice and copying
2842 nsCString mData;
2843
2844 private:
2845 // Note: we can be destroyed off the target thread, so be careful not to let
2846 // this get Released()ed on the temp thread!
2847 RefPtr<DataChannelConnection> mConnection;
2848 uint16_t mStream;
2849};
2850
2851void DataChannelConnection::SetState(DataChannelConnectionState aState) {
2852 mLock.AssertCurrentThreadOwns();
2853
2854 DC_DEBUG(do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "DataChannelConnection labeled %s (%p) switching connection state %s -> "
"%s", mTransportId.c_str(), this, ToString(mState), ToString
(aState)); } } while (0)
2855 ("DataChannelConnection labeled %s (%p) switching connection state %s -> "do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "DataChannelConnection labeled %s (%p) switching connection state %s -> "
"%s", mTransportId.c_str(), this, ToString(mState), ToString
(aState)); } } while (0)
2856 "%s",do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "DataChannelConnection labeled %s (%p) switching connection state %s -> "
"%s", mTransportId.c_str(), this, ToString(mState), ToString
(aState)); } } while (0)
2857 mTransportId.c_str(), this, ToString(mState), ToString(aState)))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "DataChannelConnection labeled %s (%p) switching connection state %s -> "
"%s", mTransportId.c_str(), this, ToString(mState), ToString
(aState)); } } while (0)
;
2858
2859 mState = aState;
2860}
2861
2862void DataChannelConnection::ReadBlob(
2863 already_AddRefed<DataChannelConnection> aThis, uint16_t aStream,
2864 nsIInputStream* aBlob) {
2865 // NOTE: 'aThis' has been forgotten by the caller to avoid releasing
2866 // it off mainthread; if PeerConnectionImpl has released then we want
2867 // ~DataChannelConnection() to run on MainThread
2868
2869 // XXX to do this safely, we must enqueue these atomically onto the
2870 // output socket. We need a sender thread(s?) to enqueue data into the
2871 // socket and to avoid main-thread IO that might block. Even on a
2872 // background thread, we may not want to block on one stream's data.
2873 // I.e. run non-blocking and service multiple channels.
2874
2875 // Must not let Dispatching it cause the DataChannelConnection to get
2876 // released on the wrong thread. Using
2877 // WrapRunnable(RefPtr<DataChannelConnection>(aThis),... will occasionally
2878 // cause aThis to get released on this thread. Also, an explicit Runnable
2879 // lets us avoid copying the blob data an extra time.
2880 RefPtr<DataChannelBlobSendRunnable> runnable =
2881 new DataChannelBlobSendRunnable(aThis, aStream);
2882 // avoid copying the blob data by passing the mData from the runnable
2883 if (NS_FAILED(NS_ReadInputStreamToString(aBlob, runnable->mData, -1))((bool)(__builtin_expect(!!(NS_FAILED_impl(NS_ReadInputStreamToString
(aBlob, runnable->mData, -1))), 0)))
) {
2884 // Bug 966602: Doesn't return an error to the caller via onerror.
2885 // We must release DataChannelConnection on MainThread to avoid issues (bug
2886 // 876167) aThis is now owned by the runnable; release it there
2887 NS_ReleaseOnMainThread("DataChannelBlobSendRunnable", runnable.forget());
2888 return;
2889 }
2890 aBlob->Close();
2891 Dispatch(runnable.forget());
2892}
2893
2894// Returns a POSIX error code.
2895int DataChannelConnection::SendDataMsgCommon(uint16_t stream,
2896 const nsACString& aMsg,
2897 bool isBinary) {
2898 ASSERT_WEBRTC(NS_IsMainThread())do { static_assert( mozilla::detail::AssertionConditionType<
decltype((NS_IsMainThread()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((NS_IsMainThread())))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("(NS_IsMainThread())"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 2898); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(NS_IsMainThread())"
")"); do { *((volatile int*)__null) = 2898; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2899 // We really could allow this from other threads, so long as we deal with
2900 // asynchronosity issues with channels closing, in particular access to
2901 // mChannels, and issues with the association closing (access to mSocket).
2902
2903 const uint8_t* data = (const uint8_t*)aMsg.BeginReading();
2904 uint32_t len = aMsg.Length();
2905#if (UINT32_MAX(4294967295U) > SIZE_MAX(18446744073709551615UL))
2906 if (len > SIZE_MAX(18446744073709551615UL)) {
2907 return EMSGSIZE90;
2908 }
2909#endif
2910
2911 DC_DEBUG(("Sending %sto stream %u: %u bytes", isBinary ? "binary " : "",do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Sending %sto stream %u: %u bytes"
, isBinary ? "binary " : "", stream, len); } } while (0)
2912 stream, len))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Sending %sto stream %u: %u bytes"
, isBinary ? "binary " : "", stream, len); } } while (0)
;
2913 // XXX if we want more efficiency, translate flags once at open time
2914 RefPtr<DataChannel> channelPtr = mChannels.Get(stream);
2915 if (NS_WARN_IF(!channelPtr)NS_warn_if_impl(!channelPtr, "!channelPtr", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 2915)
) {
2916 return EINVAL22; // TODO: Find a better error code
2917 }
2918 bool is_empty = len == 0;
2919 const uint8_t byte = 0;
2920 if (is_empty) {
2921 data = &byte;
2922 len = 1;
2923 }
2924 auto& channel = *channelPtr;
2925 int err = 0;
2926 MutexAutoLock lock(mLock);
2927 if (isBinary) {
2928 err = SendDataMsg(
2929 channel, data, len, DATA_CHANNEL_PPID_BINARY_PARTIAL52,
2930 is_empty ? DATA_CHANNEL_PPID_BINARY_EMPTY57 : DATA_CHANNEL_PPID_BINARY53);
2931 } else {
2932 err = SendDataMsg(channel, data, len, DATA_CHANNEL_PPID_DOMSTRING_PARTIAL54,
2933 is_empty ? DATA_CHANNEL_PPID_DOMSTRING_EMPTY56
2934 : DATA_CHANNEL_PPID_DOMSTRING51);
2935 }
2936 if (!err) {
2937 channel.WithTrafficCounters([&len](DataChannel::TrafficCounters& counters) {
2938 counters.mMessagesSent++;
2939 counters.mBytesSent += len;
2940 });
2941 }
2942
2943 return err;
2944}
2945
2946void DataChannelConnection::Stop() {
2947 // Note: This will call 'CloseAll' from the main thread
2948 Dispatch(do_AddRef(new DataChannelOnMessageAvailable(
2949 DataChannelOnMessageAvailable::EventType::OnDisconnected, this)));
2950}
2951
2952void DataChannelConnection::Close(DataChannel* aChannel) {
2953 MutexAutoLock lock(mLock);
2954 CloseLocked(aChannel);
2955}
2956
2957// So we can call Close() with the lock already held
2958// Called from someone who holds a ref via ::Close(), or from ~DataChannel
2959void DataChannelConnection::CloseLocked(DataChannel* aChannel) {
2960 MOZ_ASSERT(aChannel)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aChannel)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aChannel))), 0))) { do { } while
(false); MOZ_ReportAssertionFailure("aChannel", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 2960); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aChannel" ")"
); do { *((volatile int*)__null) = 2960; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
2961 RefPtr<DataChannel> channel(aChannel); // make sure it doesn't go away on us
2962
2963 mLock.AssertCurrentThreadOwns();
2964 // Note that `aChannel->mConnection` is `this`. This is just here to satisfy
2965 // the thread safety annotations on DataChannel.
2966 aChannel->mConnection->mLock.AssertCurrentThreadOwns();
2967 DC_DEBUG(("Connection %p/Channel %p: Closing stream %u",do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Connection %p/Channel %p: Closing stream %u"
, channel->mConnection.get(), channel.get(), channel->mStream
); } } while (0)
2968 channel->mConnection.get(), channel.get(), channel->mStream))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Connection %p/Channel %p: Closing stream %u"
, channel->mConnection.get(), channel.get(), channel->mStream
); } } while (0)
;
2969
2970 aChannel->mBufferedData.Clear();
2971 if (GetState() == DataChannelConnectionState::Closed) {
2972 // If we're CLOSING, we might leave this in place until we can send a
2973 // reset.
2974 mChannels.Remove(channel);
2975 }
2976 mPending.erase(channel);
2977
2978 // This is supposed to only be accessed from Main thread, but this has
2979 // been accessed here from the STS thread for a long time now.
2980 // See Bug 1586475
2981 DataChannelState channelState = aChannel->GetReadyState();
2982 // re-test since it may have closed before the lock was grabbed
2983 if (channelState == DataChannelState::Closed ||
2984 channelState == DataChannelState::Closing) {
2985 DC_DEBUG(("Channel already closing/closed (%s)", ToString(channelState)))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Channel already closing/closed (%s)"
, ToString(channelState)); } } while (0)
;
2986 return;
2987 }
2988
2989 if (channel->mStream != INVALID_STREAM(0xFFFF)) {
2990 ResetOutgoingStream(*channel);
2991 if (GetState() != DataChannelConnectionState::Closed) {
2992 // Individual channel is being closed, send reset now.
2993 SendOutgoingStreamReset();
2994 }
2995 }
2996 aChannel->SetReadyState(DataChannelState::Closing);
2997 if (GetState() == DataChannelConnectionState::Closed) {
2998 // we're not going to hang around waiting
2999 channel->StreamClosedLocked();
3000 }
3001 // At this point when we leave here, the object is a zombie held alive only by
3002 // the DOM object
3003}
3004
3005void DataChannelConnection::CloseAll() {
3006 DC_DEBUG(("Closing all channels (connection %p)", (void*)this))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Closing all channels (connection %p)"
, (void*)this); } } while (0)
;
3007
3008 // Make sure no more channels will be opened
3009 MutexAutoLock lock(mLock);
3010 SetState(DataChannelConnectionState::Closed);
3011
3012 // Close current channels
3013 // If there are runnables, they hold a strong ref and keep the channel
3014 // and/or connection alive (even if in a CLOSED state)
3015 for (auto& channel : mChannels.GetAll()) {
3016 MutexAutoUnlock lock(mLock);
3017 channel->Close();
3018 }
3019
3020 // Clean up any pending opens for channels
3021 std::set<RefPtr<DataChannel>> temp(std::move(mPending));
3022 for (const auto& channel : temp) {
3023 DC_DEBUG(("closing pending channel %p, stream %u", channel.get(),do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "closing pending channel %p, stream %u"
, channel.get(), channel->mStream); } } while (0)
3024 channel->mStream))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "closing pending channel %p, stream %u"
, channel.get(), channel->mStream); } } while (0)
;
3025 MutexAutoUnlock lock(mLock);
3026 channel->Close(); // also releases the ref on each iteration
3027 }
3028 // It's more efficient to let the Resets queue in shutdown and then
3029 // SendOutgoingStreamReset() here.
3030 SendOutgoingStreamReset();
3031}
3032
3033bool DataChannelConnection::Channels::IdComparator::Equals(
3034 const RefPtr<DataChannel>& aChannel, uint16_t aId) const {
3035 return aChannel->mStream == aId;
3036}
3037
3038bool DataChannelConnection::Channels::IdComparator::LessThan(
3039 const RefPtr<DataChannel>& aChannel, uint16_t aId) const {
3040 return aChannel->mStream < aId;
3041}
3042
3043bool DataChannelConnection::Channels::IdComparator::Equals(
3044 const RefPtr<DataChannel>& a1, const RefPtr<DataChannel>& a2) const {
3045 return Equals(a1, a2->mStream);
3046}
3047
3048bool DataChannelConnection::Channels::IdComparator::LessThan(
3049 const RefPtr<DataChannel>& a1, const RefPtr<DataChannel>& a2) const {
3050 return LessThan(a1, a2->mStream);
3051}
3052
3053void DataChannelConnection::Channels::Insert(
3054 const RefPtr<DataChannel>& aChannel) {
3055 DC_DEBUG(("Inserting channel %u : %p", aChannel->mStream, aChannel.get()))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Inserting channel %u : %p"
, aChannel->mStream, aChannel.get()); } } while (0)
;
3056 MutexAutoLock lock(mMutex);
3057 if (aChannel->mStream != INVALID_STREAM(0xFFFF)) {
3058 MOZ_ASSERT(!mChannels.ContainsSorted(aChannel, IdComparator()))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!mChannels.ContainsSorted(aChannel, IdComparator()))
>::isValid, "invalid assertion condition"); if ((__builtin_expect
(!!(!(!!(!mChannels.ContainsSorted(aChannel, IdComparator()))
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("!mChannels.ContainsSorted(aChannel, IdComparator())"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 3058); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mChannels.ContainsSorted(aChannel, IdComparator())"
")"); do { *((volatile int*)__null) = 3058; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3059 }
3060
3061 MOZ_ASSERT(!mChannels.Contains(aChannel))do { static_assert( mozilla::detail::AssertionConditionType<
decltype(!mChannels.Contains(aChannel))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(!mChannels.Contains(aChannel
)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!mChannels.Contains(aChannel)", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 3061); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mChannels.Contains(aChannel)"
")"); do { *((volatile int*)__null) = 3061; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3062
3063 mChannels.InsertElementSorted(aChannel, IdComparator());
3064}
3065
3066bool DataChannelConnection::Channels::Remove(
3067 const RefPtr<DataChannel>& aChannel) {
3068 DC_DEBUG(("Removing channel %u : %p", aChannel->mStream, aChannel.get()))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Removing channel %u : %p"
, aChannel->mStream, aChannel.get()); } } while (0)
;
3069 MutexAutoLock lock(mMutex);
3070 if (aChannel->mStream == INVALID_STREAM(0xFFFF)) {
3071 return mChannels.RemoveElement(aChannel);
3072 }
3073
3074 return mChannels.RemoveElementSorted(aChannel, IdComparator());
3075}
3076
3077RefPtr<DataChannel> DataChannelConnection::Channels::Get(uint16_t aId) const {
3078 MutexAutoLock lock(mMutex);
3079 auto index = mChannels.BinaryIndexOf(aId, IdComparator());
3080 if (index == ChannelArray::NoIndex) {
3081 return nullptr;
3082 }
3083 return mChannels[index];
3084}
3085
3086RefPtr<DataChannel> DataChannelConnection::Channels::GetNextChannel(
3087 uint16_t aCurrentId) const {
3088 MutexAutoLock lock(mMutex);
3089 if (mChannels.IsEmpty()) {
3090 return nullptr;
3091 }
3092
3093 auto index = mChannels.IndexOfFirstElementGt(aCurrentId, IdComparator());
3094 if (index == mChannels.Length()) {
3095 index = 0;
3096 }
3097 return mChannels[index];
3098}
3099
3100DataChannel::DataChannel(DataChannelConnection* connection, uint16_t stream,
3101 DataChannelState state, const nsACString& label,
3102 const nsACString& protocol,
3103 DataChannelReliabilityPolicy policy, uint32_t value,
3104 bool ordered, bool negotiated,
3105 DataChannelListener* aListener, nsISupports* aContext)
3106 : mListener(aListener),
3107 mContext(aContext),
3108 mConnection(connection),
3109 mLabel(label),
3110 mProtocol(protocol),
3111 mReadyState(state),
3112 mStream(stream),
3113 mPrPolicy(policy),
3114 mPrValue(value),
3115 mNegotiated(negotiated),
3116 mOrdered(ordered),
3117 mIsRecvBinary(false),
3118 mBufferedThreshold(0), // default from spec
3119 mBufferedAmount(0),
3120 mMainThreadEventTarget(connection->GetNeckoTarget()),
3121 mStatsLock("netwer::sctp::DataChannel::mStatsLock") {
3122 NS_ASSERTION(mConnection, "NULL connection")do { if (!(mConnection)) { NS_DebugBreak(NS_DEBUG_ASSERTION, "NULL connection"
, "mConnection", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 3122); MOZ_PretendNoReturn(); } } while (0)
;
3123}
3124
3125DataChannel::~DataChannel() {
3126 // NS_ASSERTION since this is more "I think I caught all the cases that
3127 // can cause this" than a true kill-the-program assertion. If this is
3128 // wrong, nothing bad happens. A worst it's a leak.
3129 NS_ASSERTION(mReadyState == DataChannelState::Closed ||do { if (!(mReadyState == DataChannelState::Closed || mReadyState
== DataChannelState::Closing)) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "unexpected state in ~DataChannel", "mReadyState == DataChannelState::Closed || mReadyState == DataChannelState::Closing"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 3131); MOZ_PretendNoReturn(); } } while (0)
3130 mReadyState == DataChannelState::Closing,do { if (!(mReadyState == DataChannelState::Closed || mReadyState
== DataChannelState::Closing)) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "unexpected state in ~DataChannel", "mReadyState == DataChannelState::Closed || mReadyState == DataChannelState::Closing"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 3131); MOZ_PretendNoReturn(); } } while (0)
3131 "unexpected state in ~DataChannel")do { if (!(mReadyState == DataChannelState::Closed || mReadyState
== DataChannelState::Closing)) { NS_DebugBreak(NS_DEBUG_ASSERTION
, "unexpected state in ~DataChannel", "mReadyState == DataChannelState::Closed || mReadyState == DataChannelState::Closing"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 3131); MOZ_PretendNoReturn(); } } while (0)
;
3132}
3133
3134void DataChannel::Close() {
3135 if (mConnection) {
3136 // ensure we don't get deleted
3137 RefPtr<DataChannelConnection> connection(mConnection);
3138 connection->Close(this);
3139 }
3140}
3141
3142// Used when disconnecting from the DataChannelConnection
3143void DataChannel::StreamClosedLocked() {
3144 MOZ_ASSERT(mConnection)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mConnection)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mConnection))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("mConnection", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 3144); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mConnection"
")"); do { *((volatile int*)__null) = 3144; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3145 if (!mConnection) {
3146 return;
3147 }
3148 mConnection->mLock.AssertCurrentThreadOwns();
3149
3150 DC_DEBUG(("Destroying Data channel %u", mStream))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "Destroying Data channel %u"
, mStream); } } while (0)
;
3151 MOZ_ASSERT_IF(mStream != INVALID_STREAM,do { if (mStream != (0xFFFF)) { do { static_assert( mozilla::
detail::AssertionConditionType<decltype(!mConnection->FindChannelByStream
(mStream))>::isValid, "invalid assertion condition"); if (
(__builtin_expect(!!(!(!!(!mConnection->FindChannelByStream
(mStream)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!mConnection->FindChannelByStream(mStream)", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 3152); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mConnection->FindChannelByStream(mStream)"
")"); do { *((volatile int*)__null) = 3152; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
3152 !mConnection->FindChannelByStream(mStream))do { if (mStream != (0xFFFF)) { do { static_assert( mozilla::
detail::AssertionConditionType<decltype(!mConnection->FindChannelByStream
(mStream))>::isValid, "invalid assertion condition"); if (
(__builtin_expect(!!(!(!!(!mConnection->FindChannelByStream
(mStream)))), 0))) { do { } while (false); MOZ_ReportAssertionFailure
("!mConnection->FindChannelByStream(mStream)", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 3152); AnnotateMozCrashReason("MOZ_ASSERT" "(" "!mConnection->FindChannelByStream(mStream)"
")"); do { *((volatile int*)__null) = 3152; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false); } } while (
false)
;
3153 AnnounceClosed();
3154 // We leave mConnection live until the DOM releases us, to avoid races
3155}
3156
3157void DataChannel::ReleaseConnection() {
3158 ASSERT_WEBRTC(NS_IsMainThread())do { static_assert( mozilla::detail::AssertionConditionType<
decltype((NS_IsMainThread()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((NS_IsMainThread())))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("(NS_IsMainThread())"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 3158); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(NS_IsMainThread())"
")"); do { *((volatile int*)__null) = 3158; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3159 mConnection = nullptr;
3160}
3161
3162void DataChannel::SetListener(DataChannelListener* aListener,
3163 nsISupports* aContext) {
3164 ASSERT_WEBRTC(NS_IsMainThread())do { static_assert( mozilla::detail::AssertionConditionType<
decltype((NS_IsMainThread()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((NS_IsMainThread())))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("(NS_IsMainThread())"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 3164); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(NS_IsMainThread())"
")"); do { *((volatile int*)__null) = 3164; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3165 mContext = aContext;
3166 mListener = aListener;
3167}
3168
3169void DataChannel::SendErrnoToErrorResult(int error, size_t aMessageSize,
3170 ErrorResult& aRv) {
3171 switch (error) {
3172 case 0:
3173 break;
3174 case EMSGSIZE90: {
3175 nsPrintfCString err("Message size (%zu) exceeds maxMessageSize",
3176 aMessageSize);
3177 aRv.ThrowTypeError(err);
3178 break;
3179 }
3180 default:
3181 aRv.Throw(NS_ERROR_DOM_OPERATION_ERR);
3182 break;
3183 }
3184}
3185
3186void DataChannel::IncrementBufferedAmount(uint32_t aSize, ErrorResult& aRv) {
3187 ASSERT_WEBRTC(NS_IsMainThread())do { static_assert( mozilla::detail::AssertionConditionType<
decltype((NS_IsMainThread()))>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!((NS_IsMainThread())))), 0)))
{ do { } while (false); MOZ_ReportAssertionFailure("(NS_IsMainThread())"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 3187); AnnotateMozCrashReason("MOZ_ASSERT" "(" "(NS_IsMainThread())"
")"); do { *((volatile int*)__null) = 3187; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3188 if (mBufferedAmount > UINT32_MAX(4294967295U) - aSize) {
3189 aRv.Throw(NS_ERROR_FILE_TOO_BIG);
3190 return;
3191 }
3192
3193 mBufferedAmount += aSize;
3194}
3195
3196void DataChannel::DecrementBufferedAmount(uint32_t aSize) {
3197 mMainThreadEventTarget->Dispatch(NS_NewRunnableFunction(
3198 "DataChannel::DecrementBufferedAmount",
3199 [this, self = RefPtr<DataChannel>(this), aSize] {
3200 MOZ_ASSERT(aSize <= mBufferedAmount)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(aSize <= mBufferedAmount)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(aSize <= mBufferedAmount)
)), 0))) { do { } while (false); MOZ_ReportAssertionFailure("aSize <= mBufferedAmount"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 3200); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aSize <= mBufferedAmount"
")"); do { *((volatile int*)__null) = 3200; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3201 bool wasLow = mBufferedAmount <= mBufferedThreshold;
3202 mBufferedAmount -= aSize;
3203 if (!wasLow && mBufferedAmount <= mBufferedThreshold) {
3204 DC_DEBUG(("%s: sending BUFFER_LOW_THRESHOLD for %s/%s: %u",do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "%s: sending BUFFER_LOW_THRESHOLD for %s/%s: %u"
, __FUNCTION__, mLabel.get(), mProtocol.get(), mStream); } } while
(0)
3205 __FUNCTION__, mLabel.get(), mProtocol.get(), mStream))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "%s: sending BUFFER_LOW_THRESHOLD for %s/%s: %u"
, __FUNCTION__, mLabel.get(), mProtocol.get(), mStream); } } while
(0)
;
3206 mListener->OnBufferLow(mContext);
3207 }
3208 if (mBufferedAmount == 0) {
3209 DC_DEBUG(("%s: sending NO_LONGER_BUFFERED for %s/%s: %u",do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "%s: sending NO_LONGER_BUFFERED for %s/%s: %u"
, __FUNCTION__, mLabel.get(), mProtocol.get(), mStream); } } while
(0)
3210 __FUNCTION__, mLabel.get(), mProtocol.get(), mStream))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "%s: sending NO_LONGER_BUFFERED for %s/%s: %u"
, __FUNCTION__, mLabel.get(), mProtocol.get(), mStream); } } while
(0)
;
3211 mListener->NotBuffered(mContext);
3212 }
3213 }));
3214}
3215
3216void DataChannel::AnnounceOpen() {
3217 mMainThreadEventTarget->Dispatch(NS_NewRunnableFunction(
3218 "DataChannel::AnnounceOpen", [this, self = RefPtr<DataChannel>(this)] {
3219 DataChannelState state = GetReadyState();
3220 // Special-case; spec says to put brand-new remote-created DataChannel
3221 // in "open", but queue the firing of the "open" event.
3222 if (state != DataChannelState::Closing &&
3223 state != DataChannelState::Closed) {
3224 if (!mEverOpened && mConnection && mConnection->mListener) {
3225 mEverOpened = true;
3226 mConnection->mListener->NotifyDataChannelOpen(this);
3227 }
3228 SetReadyState(DataChannelState::Open);
3229 DC_DEBUG(("%s: sending ON_CHANNEL_OPEN for %s/%s: %u", __FUNCTION__,do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "%s: sending ON_CHANNEL_OPEN for %s/%s: %u"
, __FUNCTION__, mLabel.get(), mProtocol.get(), mStream); } } while
(0)
3230 mLabel.get(), mProtocol.get(), mStream))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "%s: sending ON_CHANNEL_OPEN for %s/%s: %u"
, __FUNCTION__, mLabel.get(), mProtocol.get(), mStream); } } while
(0)
;
3231 if (mListener) {
3232 mListener->OnChannelConnected(mContext);
3233 }
3234 }
3235 }));
3236}
3237
3238void DataChannel::AnnounceClosed() {
3239 mMainThreadEventTarget->Dispatch(NS_NewRunnableFunction(
3240 "DataChannel::AnnounceClosed", [this, self = RefPtr<DataChannel>(this)] {
3241 if (GetReadyState() == DataChannelState::Closed) {
3242 return;
3243 }
3244 if (mEverOpened && mConnection && mConnection->mListener) {
3245 mConnection->mListener->NotifyDataChannelClosed(this);
3246 }
3247 SetReadyState(DataChannelState::Closed);
3248 MOZ_PUSH_IGNORE_THREAD_SAFETYGCC diagnostic push GCC diagnostic ignored "-Wthread-safety"
3249 // Ignoring thread safety here because the analysis needs us to lock
3250 // mConnection->mLock when mConnection may be null.
3251 mBufferedData.Clear();
3252 MOZ_POP_THREAD_SAFETYGCC diagnostic pop
3253 if (mListener) {
3254 DC_DEBUG(("%s: sending ON_CHANNEL_CLOSED for %s/%s: %u", __FUNCTION__,do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "%s: sending ON_CHANNEL_CLOSED for %s/%s: %u"
, __FUNCTION__, mLabel.get(), mProtocol.get(), mStream); } } while
(0)
3255 mLabel.get(), mProtocol.get(), mStream))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "%s: sending ON_CHANNEL_CLOSED for %s/%s: %u"
, __FUNCTION__, mLabel.get(), mProtocol.get(), mStream); } } while
(0)
;
3256 mListener->OnChannelClosed(mContext);
3257 }
3258 }));
3259}
3260
3261// Set ready state
3262void DataChannel::SetReadyState(const DataChannelState aState) {
3263 MOZ_ASSERT(NS_IsMainThread())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(NS_IsMainThread())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(NS_IsMainThread()))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("NS_IsMainThread()"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 3263); AnnotateMozCrashReason("MOZ_ASSERT" "(" "NS_IsMainThread()"
")"); do { *((volatile int*)__null) = 3263; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3264
3265 DC_DEBUG(do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "DataChannelConnection labeled %s(%p) (stream %d) changing ready state "
"%s -> %s", mLabel.get(), this, mStream, ToString(mReadyState
), ToString(aState)); } } while (0)
3266 ("DataChannelConnection labeled %s(%p) (stream %d) changing ready state "do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "DataChannelConnection labeled %s(%p) (stream %d) changing ready state "
"%s -> %s", mLabel.get(), this, mStream, ToString(mReadyState
), ToString(aState)); } } while (0)
3267 "%s -> %s",do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "DataChannelConnection labeled %s(%p) (stream %d) changing ready state "
"%s -> %s", mLabel.get(), this, mStream, ToString(mReadyState
), ToString(aState)); } } while (0)
3268 mLabel.get(), this, mStream, ToString(mReadyState), ToString(aState)))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Debug)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Debug, "DataChannelConnection labeled %s(%p) (stream %d) changing ready state "
"%s -> %s", mLabel.get(), this, mStream, ToString(mReadyState
), ToString(aState)); } } while (0)
;
3269
3270 mReadyState = aState;
3271}
3272
3273void DataChannel::SendMsg(const nsACString& aMsg, ErrorResult& aRv) {
3274 if (!EnsureValidStream(aRv)) {
3275 return;
3276 }
3277
3278 SendErrnoToErrorResult(mConnection->SendMsg(mStream, aMsg), aMsg.Length(),
3279 aRv);
3280 if (!aRv.Failed()) {
3281 IncrementBufferedAmount(aMsg.Length(), aRv);
3282 }
3283}
3284
3285void DataChannel::SendBinaryMsg(const nsACString& aMsg, ErrorResult& aRv) {
3286 if (!EnsureValidStream(aRv)) {
3287 return;
3288 }
3289
3290 SendErrnoToErrorResult(mConnection->SendBinaryMsg(mStream, aMsg),
3291 aMsg.Length(), aRv);
3292 if (!aRv.Failed()) {
3293 IncrementBufferedAmount(aMsg.Length(), aRv);
3294 }
3295}
3296
3297void DataChannel::SendBinaryBlob(dom::Blob& aBlob, ErrorResult& aRv) {
3298 if (!EnsureValidStream(aRv)) {
3299 return;
3300 }
3301
3302 uint64_t msgLength = aBlob.GetSize(aRv);
3303 if (aRv.Failed()) {
3304 return;
3305 }
3306
3307 if (msgLength > UINT32_MAX(4294967295U)) {
3308 aRv.Throw(NS_ERROR_FILE_TOO_BIG);
3309 return;
3310 }
3311
3312 // We convert to an nsIInputStream here, because Blob is not threadsafe, and
3313 // we don't convert it earlier because we need to know how large this is so we
3314 // can update bufferedAmount.
3315 nsCOMPtr<nsIInputStream> msgStream;
3316 aBlob.CreateInputStream(getter_AddRefs(msgStream), aRv);
3317 if (NS_WARN_IF(aRv.Failed())NS_warn_if_impl(aRv.Failed(), "aRv.Failed()", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 3317)
) {
3318 return;
3319 }
3320
3321 SendErrnoToErrorResult(mConnection->SendBlob(mStream, msgStream), msgLength,
3322 aRv);
3323 if (!aRv.Failed()) {
3324 IncrementBufferedAmount(msgLength, aRv);
3325 }
3326}
3327
3328dom::Nullable<uint16_t> DataChannel::GetMaxPacketLifeTime() const {
3329 if (mPrPolicy == DataChannelReliabilityPolicy::LimitedLifetime) {
3330 return dom::Nullable<uint16_t>(mPrValue);
3331 }
3332 return dom::Nullable<uint16_t>();
3333}
3334
3335dom::Nullable<uint16_t> DataChannel::GetMaxRetransmits() const {
3336 if (mPrPolicy == DataChannelReliabilityPolicy::LimitedRetransmissions) {
3337 return dom::Nullable<uint16_t>(mPrValue);
3338 }
3339 return dom::Nullable<uint16_t>();
3340}
3341
3342uint32_t DataChannel::GetBufferedAmountLowThreshold() const {
3343 return mBufferedThreshold;
3344}
3345
3346// Never fire immediately, as it's defined to fire on transitions, not state
3347void DataChannel::SetBufferedAmountLowThreshold(uint32_t aThreshold) {
3348 mBufferedThreshold = aThreshold;
3349}
3350
3351void DataChannel::SendOrQueue(DataChannelOnMessageAvailable* aMessage) {
3352 nsCOMPtr<nsIRunnable> runnable = aMessage;
3353 mMainThreadEventTarget->Dispatch(runnable.forget());
3354}
3355
3356DataChannel::TrafficCounters DataChannel::GetTrafficCounters() const {
3357 MutexAutoLock lock(mStatsLock);
3358 return mTrafficCounters;
3359}
3360
3361bool DataChannel::EnsureValidStream(ErrorResult& aRv) {
3362 MOZ_ASSERT(mConnection)do { static_assert( mozilla::detail::AssertionConditionType<
decltype(mConnection)>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(mConnection))), 0))) { do { }
while (false); MOZ_ReportAssertionFailure("mConnection", "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 3362); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mConnection"
")"); do { *((volatile int*)__null) = 3362; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3363 if (mConnection && mStream != INVALID_STREAM(0xFFFF)) {
3364 return true;
3365 }
3366 aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
3367 return false;
3368}
3369
3370void DataChannel::WithTrafficCounters(
3371 const std::function<void(TrafficCounters&)>& aFn) {
3372 MutexAutoLock lock(mStatsLock);
3373 aFn(mTrafficCounters);
3374}
3375
3376nsresult DataChannelOnMessageAvailable::Run() {
3377 MOZ_ASSERT(NS_IsMainThread())do { static_assert( mozilla::detail::AssertionConditionType<
decltype(NS_IsMainThread())>::isValid, "invalid assertion condition"
); if ((__builtin_expect(!!(!(!!(NS_IsMainThread()))), 0))) {
do { } while (false); MOZ_ReportAssertionFailure("NS_IsMainThread()"
, "/var/lib/jenkins/workspace/firefox-scan-build/netwerk/sctp/datachannel/DataChannel.cpp"
, 3377); AnnotateMozCrashReason("MOZ_ASSERT" "(" "NS_IsMainThread()"
")"); do { *((volatile int*)__null) = 3377; __attribute__((nomerge
)) ::abort(); } while (false); } } while (false)
;
3378
3379 // Note: calling the listeners can indirectly cause the listeners to be
3380 // made available for GC (by removing event listeners), especially for
3381 // OnChannelClosed(). We hold a ref to the Channel and the listener
3382 // while calling this.
3383 switch (mType) {
3384 case EventType::OnDataString:
3385 case EventType::OnDataBinary:
3386 if (!mChannel->mListener) {
3387 DC_ERROR(("DataChannelOnMessageAvailable (%s) with null Listener!",do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "DataChannelOnMessageAvailable (%s) with null Listener!"
, ToString(mType)); } } while (0)
3388 ToString(mType)))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "DataChannelOnMessageAvailable (%s) with null Listener!"
, ToString(mType)); } } while (0)
;
3389 return NS_OK;
3390 }
3391
3392 if (mChannel->GetReadyState() == DataChannelState::Closed ||
3393 mChannel->GetReadyState() == DataChannelState::Closing) {
3394 // Closed by JS, probably
3395 return NS_OK;
3396 }
3397
3398 if (mType == EventType::OnDataString) {
3399 mChannel->mListener->OnMessageAvailable(mChannel->mContext, mData);
3400 } else {
3401 mChannel->mListener->OnBinaryMessageAvailable(mChannel->mContext,
3402 mData);
3403 }
3404 break;
3405 case EventType::OnDisconnected:
3406 // If we've disconnected, make sure we close all the streams - from
3407 // mainthread!
3408 if (mConnection->mListener) {
3409 mConnection->mListener->NotifySctpClosed();
3410 }
3411 mConnection->CloseAll();
3412 break;
3413 case EventType::OnChannelCreated:
3414 if (!mConnection->mListener) {
3415 DC_ERROR(("DataChannelOnMessageAvailable (%s) with null Listener!",do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "DataChannelOnMessageAvailable (%s) with null Listener!"
, ToString(mType)); } } while (0)
3416 ToString(mType)))do { const ::mozilla::LogModule* moz_real_module = mozilla::gDataChannelLog
; if ((__builtin_expect(!!(mozilla::detail::log_test(moz_real_module
, mozilla::LogLevel::Error)), 0))) { mozilla::detail::log_print
(moz_real_module, mozilla::LogLevel::Error, "DataChannelOnMessageAvailable (%s) with null Listener!"
, ToString(mType)); } } while (0)
;
3417 return NS_OK;
3418 }
3419
3420 // important to give it an already_AddRefed pointer!
3421 mConnection->mListener->NotifyDataChannel(mChannel.forget());
3422 break;
3423 case EventType::OnConnection:
3424 if (mConnection->mListener) {
3425 mConnection->mListener->NotifySctpConnected();
3426 }
3427 break;
3428 }
3429 return NS_OK;
3430}
3431
3432} // namespace mozilla