Thu, 15 Jan 2015 15:55:04 +0100
Back out 97036ab72558 which inappropriately compared turds to third parties.
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/. */
7 #ifndef NETWERK_SCTP_DATACHANNEL_DATACHANNEL_H_
8 #define NETWERK_SCTP_DATACHANNEL_DATACHANNEL_H_
10 #ifdef MOZ_WEBRTC_SIGNALING
11 #define SCTP_DTLS_SUPPORTED 1
12 #endif
14 #include <string>
15 #include <errno.h>
16 #include "nsISupports.h"
17 #include "nsCOMPtr.h"
18 #include "mozilla/WeakPtr.h"
19 #include "nsString.h"
20 #include "nsThreadUtils.h"
21 #include "nsTArray.h"
22 #include "nsDeque.h"
23 #include "nsIInputStream.h"
24 #include "nsITimer.h"
25 #include "mozilla/Mutex.h"
26 #include "DataChannelProtocol.h"
27 #include "DataChannelListener.h"
28 #ifdef SCTP_DTLS_SUPPORTED
29 #include "mtransport/sigslot.h"
30 #include "mtransport/transportflow.h"
31 #include "mtransport/transportlayer.h"
32 #include "mtransport/transportlayerdtls.h"
33 #include "mtransport/transportlayerprsock.h"
34 #endif
36 #ifndef DATACHANNEL_LOG
37 #define DATACHANNEL_LOG(args)
38 #endif
40 #ifndef EALREADY
41 #define EALREADY WSAEALREADY
42 #endif
44 extern "C" {
45 struct socket;
46 struct sctp_rcvinfo;
47 }
49 namespace mozilla {
51 class DTLSConnection;
52 class DataChannelConnection;
53 class DataChannel;
54 class DataChannelOnMessageAvailable;
56 // For queuing outgoing messages
57 class BufferedMsg
58 {
59 public:
60 BufferedMsg(struct sctp_sendv_spa &spa,const char *data,
61 uint32_t length);
62 ~BufferedMsg();
64 struct sctp_sendv_spa *mSpa;
65 const char *mData;
66 uint32_t mLength;
67 };
69 // for queuing incoming data messages before the Open or
70 // external negotiation is indicated to us
71 class QueuedDataMessage
72 {
73 public:
74 QueuedDataMessage(uint16_t stream, uint32_t ppid,
75 const void *data, size_t length)
76 : mStream(stream)
77 , mPpid(ppid)
78 , mLength(length)
79 {
80 mData = static_cast<char *>(moz_xmalloc(length)); // infallible
81 memcpy(mData, data, length);
82 }
84 ~QueuedDataMessage()
85 {
86 moz_free(mData);
87 }
89 uint16_t mStream;
90 uint32_t mPpid;
91 size_t mLength;
92 char *mData;
93 };
95 // One per PeerConnection
96 class DataChannelConnection: public nsITimerCallback
97 #ifdef SCTP_DTLS_SUPPORTED
98 , public sigslot::has_slots<>
99 #endif
100 {
101 public:
102 NS_DECL_THREADSAFE_ISUPPORTS
103 NS_DECL_NSITIMERCALLBACK
105 class DataConnectionListener : public SupportsWeakPtr<DataConnectionListener>
106 {
107 public:
108 MOZ_DECLARE_REFCOUNTED_TYPENAME(DataChannelConnection::DataConnectionListener)
109 virtual ~DataConnectionListener() {}
111 // Called when a the connection is open
112 virtual void NotifyConnection() = 0;
114 // Called when a the connection is lost/closed
115 virtual void NotifyClosedConnection() = 0;
117 // Called when a new DataChannel has been opened by the other side.
118 virtual void NotifyDataChannel(already_AddRefed<DataChannel> channel) = 0;
119 };
121 DataChannelConnection(DataConnectionListener *listener);
122 virtual ~DataChannelConnection();
124 bool Init(unsigned short aPort, uint16_t aNumStreams, bool aUsingDtls);
125 void Destroy(); // So we can spawn refs tied to runnables in shutdown
126 // Finish Destroy on STS to avoid SCTP race condition with ABORT from far end
127 void DestroyOnSTS(struct socket *aMasterSocket,
128 struct socket *aSocket);
130 #ifdef ALLOW_DIRECT_SCTP_LISTEN_CONNECT
131 // These block; they require something to decide on listener/connector
132 // (though you can do simultaneous Connect()). Do not call these from
133 // the main thread!
134 bool Listen(unsigned short port);
135 bool Connect(const char *addr, unsigned short port);
136 #endif
138 #ifdef SCTP_DTLS_SUPPORTED
139 // Connect using a TransportFlow (DTLS) channel
140 void SetEvenOdd();
141 bool ConnectViaTransportFlow(TransportFlow *aFlow, uint16_t localport, uint16_t remoteport);
142 void CompleteConnect(TransportFlow *flow, TransportLayer::State state);
143 void SetSignals();
144 #endif
146 typedef enum {
147 RELIABLE=0,
148 PARTIAL_RELIABLE_REXMIT = 1,
149 PARTIAL_RELIABLE_TIMED = 2
150 } Type;
152 already_AddRefed<DataChannel> Open(const nsACString& label,
153 const nsACString& protocol,
154 Type type, bool inOrder,
155 uint32_t prValue,
156 DataChannelListener *aListener,
157 nsISupports *aContext,
158 bool aExternalNegotiated,
159 uint16_t aStream) NS_WARN_UNUSED_RESULT;
161 void Close(DataChannel *aChannel);
162 // CloseInt() must be called with mLock held
163 void CloseInt(DataChannel *aChannel);
164 void CloseAll();
166 int32_t SendMsg(uint16_t stream, const nsACString &aMsg)
167 {
168 return SendMsgCommon(stream, aMsg, false);
169 }
170 int32_t SendBinaryMsg(uint16_t stream, const nsACString &aMsg)
171 {
172 return SendMsgCommon(stream, aMsg, true);
173 }
174 int32_t SendBlob(uint16_t stream, nsIInputStream *aBlob);
176 // Called on data reception from the SCTP library
177 // must(?) be public so my c->c++ trampoline can call it
178 int ReceiveCallback(struct socket* sock, void *data, size_t datalen,
179 struct sctp_rcvinfo rcv, int32_t flags);
181 // Find out state
182 enum {
183 CONNECTING = 0U,
184 OPEN = 1U,
185 CLOSING = 2U,
186 CLOSED = 3U
187 };
188 uint16_t GetReadyState() { MutexAutoLock lock(mLock); return mState; }
190 friend class DataChannel;
191 Mutex mLock;
193 void ReadBlob(already_AddRefed<DataChannelConnection> aThis, uint16_t aStream, nsIInputStream* aBlob);
195 void GetStreamIds(std::vector<uint16_t>* aStreamList);
197 protected:
198 friend class DataChannelOnMessageAvailable;
199 // Avoid cycles with PeerConnectionImpl
200 // Use from main thread only as WeakPtr is not threadsafe
201 WeakPtr<DataConnectionListener> mListener;
203 private:
204 friend class DataChannelConnectRunnable;
206 #ifdef SCTP_DTLS_SUPPORTED
207 static void DTLSConnectThread(void *data);
208 int SendPacket(const unsigned char* data, size_t len, bool release);
209 void SctpDtlsInput(TransportFlow *flow, const unsigned char *data, size_t len);
210 static int SctpDtlsOutput(void *addr, void *buffer, size_t length, uint8_t tos, uint8_t set_df);
211 #endif
212 DataChannel* FindChannelByStream(uint16_t stream);
213 uint16_t FindFreeStream();
214 bool RequestMoreStreams(int32_t aNeeded = 16);
215 int32_t SendControlMessage(void *msg, uint32_t len, uint16_t stream);
216 int32_t SendOpenRequestMessage(const nsACString& label, const nsACString& protocol,
217 uint16_t stream,
218 bool unordered, uint16_t prPolicy, uint32_t prValue);
219 int32_t SendOpenAckMessage(uint16_t stream);
220 int32_t SendMsgInternal(DataChannel *channel, const char *data,
221 uint32_t length, uint32_t ppid);
222 int32_t SendBinary(DataChannel *channel, const char *data,
223 uint32_t len, uint32_t ppid_partial, uint32_t ppid_final);
224 int32_t SendMsgCommon(uint16_t stream, const nsACString &aMsg, bool isBinary);
226 void DeliverQueuedData(uint16_t stream);
228 already_AddRefed<DataChannel> OpenFinish(already_AddRefed<DataChannel>&& aChannel);
230 void StartDefer();
231 bool SendDeferredMessages();
232 void ProcessQueuedOpens();
233 void ClearResets();
234 void SendOutgoingStreamReset();
235 void ResetOutgoingStream(uint16_t stream);
236 void HandleOpenRequestMessage(const struct rtcweb_datachannel_open_request *req,
237 size_t length,
238 uint16_t stream);
239 void HandleOpenAckMessage(const struct rtcweb_datachannel_ack *ack,
240 size_t length, uint16_t stream);
241 void HandleUnknownMessage(uint32_t ppid, size_t length, uint16_t stream);
242 void HandleDataMessage(uint32_t ppid, const void *buffer, size_t length, uint16_t stream);
243 void HandleMessage(const void *buffer, size_t length, uint32_t ppid, uint16_t stream);
244 void HandleAssociationChangeEvent(const struct sctp_assoc_change *sac);
245 void HandlePeerAddressChangeEvent(const struct sctp_paddr_change *spc);
246 void HandleRemoteErrorEvent(const struct sctp_remote_error *sre);
247 void HandleShutdownEvent(const struct sctp_shutdown_event *sse);
248 void HandleAdaptationIndication(const struct sctp_adaptation_event *sai);
249 void HandleSendFailedEvent(const struct sctp_send_failed_event *ssfe);
250 void HandleStreamResetEvent(const struct sctp_stream_reset_event *strrst);
251 void HandleStreamChangeEvent(const struct sctp_stream_change_event *strchg);
252 void HandleNotification(const union sctp_notification *notif, size_t n);
254 #ifdef SCTP_DTLS_SUPPORTED
255 bool IsSTSThread() {
256 bool on = false;
257 if (mSTS) {
258 mSTS->IsOnCurrentThread(&on);
259 }
260 return on;
261 }
262 #endif
264 // Exists solely for proxying release of the TransportFlow to the STS thread
265 static void ReleaseTransportFlow(nsRefPtr<TransportFlow> aFlow) {}
267 // Data:
268 // NOTE: while this array will auto-expand, increases in the number of
269 // channels available from the stack must be negotiated!
270 bool mAllocateEven;
271 nsAutoTArray<nsRefPtr<DataChannel>,16> mStreams;
272 nsDeque mPending; // Holds already_AddRefed<DataChannel>s -- careful!
273 // holds data that's come in before a channel is open
274 nsTArray<nsAutoPtr<QueuedDataMessage> > mQueuedData;
276 // Streams pending reset
277 nsAutoTArray<uint16_t,4> mStreamsResetting;
279 struct socket *mMasterSocket; // accessed from STS thread
280 struct socket *mSocket; // cloned from mMasterSocket on successful Connect on STS thread
281 uint16_t mState; // Protected with mLock
283 #ifdef SCTP_DTLS_SUPPORTED
284 nsRefPtr<TransportFlow> mTransportFlow;
285 nsCOMPtr<nsIEventTarget> mSTS;
286 #endif
287 uint16_t mLocalPort; // Accessed from connect thread
288 uint16_t mRemotePort;
289 bool mUsingDtls;
291 // Timer to control when we try to resend blocked messages
292 nsCOMPtr<nsITimer> mDeferredTimer;
293 uint32_t mDeferTimeout; // in ms
294 bool mTimerRunning;
295 nsCOMPtr<nsIThread> mInternalIOThread;
296 };
298 #define ENSURE_DATACONNECTION \
299 do { if (!mConnection) { DATACHANNEL_LOG(("%s: %p no connection!",__FUNCTION__, this)); return; } } while (0)
301 #define ENSURE_DATACONNECTION_RET(x) \
302 do { if (!mConnection) { DATACHANNEL_LOG(("%s: %p no connection!",__FUNCTION__, this)); return (x); } } while (0)
304 class DataChannel {
305 public:
306 enum {
307 CONNECTING = 0U,
308 OPEN = 1U,
309 CLOSING = 2U,
310 CLOSED = 3U,
311 WAITING_TO_OPEN = 4U
312 };
314 DataChannel(DataChannelConnection *connection,
315 uint16_t stream,
316 uint16_t state,
317 const nsACString& label,
318 const nsACString& protocol,
319 uint16_t policy, uint32_t value,
320 uint32_t flags,
321 DataChannelListener *aListener,
322 nsISupports *aContext)
323 : mListenerLock("netwerk::sctp::DataChannel")
324 , mListener(aListener)
325 , mContext(aContext)
326 , mConnection(connection)
327 , mLabel(label)
328 , mProtocol(protocol)
329 , mState(state)
330 , mReady(false)
331 , mStream(stream)
332 , mPrPolicy(policy)
333 , mPrValue(value)
334 , mFlags(flags)
335 , mIsRecvBinary(false)
336 {
337 NS_ASSERTION(mConnection,"NULL connection");
338 }
340 ~DataChannel();
341 void Destroy(); // when we disconnect from the connection after stream RESET
343 NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DataChannel)
345 // Close this DataChannel. Can be called multiple times. MUST be called
346 // before destroying the DataChannel (state must be CLOSED or CLOSING).
347 void Close();
349 // Set the listener (especially for channels created from the other side)
350 void SetListener(DataChannelListener *aListener, nsISupports *aContext);
352 // Send a string
353 bool SendMsg(const nsACString &aMsg)
354 {
355 ENSURE_DATACONNECTION_RET(false);
357 if (mStream != INVALID_STREAM)
358 return (mConnection->SendMsg(mStream, aMsg) > 0);
359 else
360 return false;
361 }
363 // Send a binary message (TypedArray)
364 bool SendBinaryMsg(const nsACString &aMsg)
365 {
366 ENSURE_DATACONNECTION_RET(false);
368 if (mStream != INVALID_STREAM)
369 return (mConnection->SendBinaryMsg(mStream, aMsg) > 0);
370 else
371 return false;
372 }
374 // Send a binary blob
375 bool SendBinaryStream(nsIInputStream *aBlob, uint32_t msgLen)
376 {
377 ENSURE_DATACONNECTION_RET(false);
379 if (mStream != INVALID_STREAM)
380 return (mConnection->SendBlob(mStream, aBlob) > 0);
381 else
382 return false;
383 }
385 uint16_t GetType() { return mPrPolicy; }
387 bool GetOrdered() { return !(mFlags & DATA_CHANNEL_FLAGS_OUT_OF_ORDER_ALLOWED); }
389 // Amount of data buffered to send
390 uint32_t GetBufferedAmount();
392 // Find out state
393 uint16_t GetReadyState()
394 {
395 if (mConnection) {
396 MutexAutoLock lock(mConnection->mLock);
397 if (mState == WAITING_TO_OPEN)
398 return CONNECTING;
399 return mState;
400 }
401 return CLOSED;
402 }
404 void GetLabel(nsAString& aLabel) { CopyUTF8toUTF16(mLabel, aLabel); }
405 void GetProtocol(nsAString& aProtocol) { CopyUTF8toUTF16(mProtocol, aProtocol); }
406 uint16_t GetStream() { return mStream; }
408 void AppReady();
410 void SendOrQueue(DataChannelOnMessageAvailable *aMessage);
412 protected:
413 Mutex mListenerLock; // protects mListener and mContext
414 DataChannelListener *mListener;
415 nsCOMPtr<nsISupports> mContext;
417 private:
418 friend class DataChannelOnMessageAvailable;
419 friend class DataChannelConnection;
421 nsresult AddDataToBinaryMsg(const char *data, uint32_t size);
423 nsRefPtr<DataChannelConnection> mConnection;
424 nsCString mLabel;
425 nsCString mProtocol;
426 uint16_t mState;
427 bool mReady;
428 uint16_t mStream;
429 uint16_t mPrPolicy;
430 uint32_t mPrValue;
431 uint32_t mFlags;
432 uint32_t mId;
433 bool mIsRecvBinary;
434 nsCString mRecvBuffer;
435 nsTArray<nsAutoPtr<BufferedMsg> > mBufferedData;
436 nsTArray<nsCOMPtr<nsIRunnable> > mQueuedMessages;
437 };
439 // used to dispatch notifications of incoming data to the main thread
440 // Patterned on CallOnMessageAvailable in WebSockets
441 // Also used to proxy other items to MainThread
442 class DataChannelOnMessageAvailable : public nsRunnable
443 {
444 public:
445 enum {
446 ON_CONNECTION,
447 ON_DISCONNECTED,
448 ON_CHANNEL_CREATED,
449 ON_CHANNEL_OPEN,
450 ON_CHANNEL_CLOSED,
451 ON_DATA,
452 START_DEFER,
453 }; /* types */
455 DataChannelOnMessageAvailable(int32_t aType,
456 DataChannelConnection *aConnection,
457 DataChannel *aChannel,
458 nsCString &aData, // XXX this causes inefficiency
459 int32_t aLen)
460 : mType(aType),
461 mChannel(aChannel),
462 mConnection(aConnection),
463 mData(aData),
464 mLen(aLen) {}
466 DataChannelOnMessageAvailable(int32_t aType,
467 DataChannel *aChannel)
468 : mType(aType),
469 mChannel(aChannel) {}
470 // XXX is it safe to leave mData/mLen uninitialized? This should only be
471 // used for notifications that don't use them, but I'd like more
472 // bulletproof compile-time checking.
474 DataChannelOnMessageAvailable(int32_t aType,
475 DataChannelConnection *aConnection,
476 DataChannel *aChannel)
477 : mType(aType),
478 mChannel(aChannel),
479 mConnection(aConnection) {}
481 // for ON_CONNECTION/ON_DISCONNECTED
482 DataChannelOnMessageAvailable(int32_t aType,
483 DataChannelConnection *aConnection,
484 bool aResult = true)
485 : mType(aType),
486 mConnection(aConnection),
487 mResult(aResult) {}
489 NS_IMETHOD Run()
490 {
491 MOZ_ASSERT(NS_IsMainThread());
492 switch (mType) {
493 case ON_DATA:
494 case ON_CHANNEL_OPEN:
495 case ON_CHANNEL_CLOSED:
496 {
497 MutexAutoLock lock(mChannel->mListenerLock);
498 if (!mChannel->mListener) {
499 DATACHANNEL_LOG(("DataChannelOnMessageAvailable (%d) with null Listener!",mType));
500 return NS_OK;
501 }
503 switch (mType) {
504 case ON_DATA:
505 if (mLen < 0) {
506 mChannel->mListener->OnMessageAvailable(mChannel->mContext, mData);
507 } else {
508 mChannel->mListener->OnBinaryMessageAvailable(mChannel->mContext, mData);
509 }
510 break;
511 case ON_CHANNEL_OPEN:
512 mChannel->mListener->OnChannelConnected(mChannel->mContext);
513 break;
514 case ON_CHANNEL_CLOSED:
515 mChannel->mListener->OnChannelClosed(mChannel->mContext);
516 break;
517 }
518 break;
519 }
520 case ON_DISCONNECTED:
521 // If we've disconnected, make sure we close all the streams - from mainthread!
522 mConnection->CloseAll();
523 // fall through
524 case ON_CHANNEL_CREATED:
525 case ON_CONNECTION:
526 // WeakPtr - only used/modified/nulled from MainThread so we can use a WeakPtr here
527 if (!mConnection->mListener) {
528 DATACHANNEL_LOG(("DataChannelOnMessageAvailable (%d) with null Listener",mType));
529 return NS_OK;
530 }
531 switch (mType) {
532 case ON_CHANNEL_CREATED:
533 // important to give it an already_AddRefed pointer!
534 mConnection->mListener->NotifyDataChannel(mChannel.forget());
535 break;
536 case ON_CONNECTION:
537 if (mResult) {
538 mConnection->mListener->NotifyConnection();
539 }
540 // FIX - on mResult false (failure) we should do something. Needs spec work here
541 break;
542 case ON_DISCONNECTED:
543 mConnection->mListener->NotifyClosedConnection();
544 break;
545 }
546 break;
547 case START_DEFER:
548 mConnection->StartDefer();
549 break;
550 }
551 return NS_OK;
552 }
554 private:
555 ~DataChannelOnMessageAvailable() {}
557 int32_t mType;
558 // XXX should use union
559 nsRefPtr<DataChannel> mChannel;
560 nsRefPtr<DataChannelConnection> mConnection;
561 nsCString mData;
562 int32_t mLen;
563 bool mResult;
564 };
566 }
568 #endif // NETWERK_SCTP_DATACHANNEL_DATACHANNEL_H_