/// <summary> /// Gets a cookie to send in an INIT ACK chunk. This SCTP /// transport for a WebRTC peer connection needs to use the same /// local tag and TSN in every chunk as only a single association /// is ever maintained. /// </summary> protected override SctpTransportCookie GetInitAckCookie( ushort sourcePort, ushort destinationPort, uint remoteTag, uint remoteTSN, uint remoteARwnd, string remoteEndPoint, int lifeTimeExtension = 0) { var cookie = new SctpTransportCookie { SourcePort = sourcePort, DestinationPort = destinationPort, RemoteTag = remoteTag, RemoteTSN = remoteTSN, RemoteARwnd = remoteARwnd, RemoteEndPoint = remoteEndPoint, Tag = RTCSctpAssociation.VerificationTag, TSN = RTCSctpAssociation.TSN, ARwnd = SctpAssociation.DEFAULT_ADVERTISED_RECEIVE_WINDOW, CreatedAt = DateTime.Now.ToString("o"), Lifetime = DEFAULT_COOKIE_LIFETIME_SECONDS + lifeTimeExtension, HMAC = string.Empty }; return(cookie); }
/// <summary> /// Create a new SCTP association instance from the cookie that was previously /// sent to the remote party in an INIT ACK chunk. /// </summary> public SctpAssociation( SctpTransport sctpTransport, SctpTransportCookie cookie, int localTransportPort) { _sctpTransport = sctpTransport; ID = $"{cookie.SourcePort}:{cookie.DestinationPort}:{localTransportPort}"; State = SctpAssociationState.Closed; GotCookie(cookie); }
/// <summary> /// Initialises the association state based on the echoed cookie (the cookie that we sent /// to the remote party and was then echoed back to us). An association can only be initialised /// from a cookie prior to it being used and prior to it ever having entered the established state. /// </summary> /// <param name="cookie">The echoed cookie that was returned from the remote party.</param> public void GotCookie(SctpTransportCookie cookie) { // The CookieEchoed state is allowed, even though a cookie should be creating a brand // new association rather than one that has already sent an INIT, in order to deal with // a race condition where both SCTP end points attempt to establish the association at // the same time using the same ports. if (_wasAborted || _wasShutdown) { logger.LogWarning($"SCTP association cannot initialise with a cookie after an abort or shutdown."); } else if (!(State == SctpAssociationState.Closed || State == SctpAssociationState.CookieEchoed)) { throw new ApplicationException($"SCTP association cannot initialise with a cookie in state {State}."); } else { _sctpSourcePort = cookie.SourcePort; _sctpDestinationPort = cookie.DestinationPort; VerificationTag = cookie.Tag; ARwnd = cookie.ARwnd; Destination = !string.IsNullOrEmpty(cookie.RemoteEndPoint) ? IPSocket.Parse(cookie.RemoteEndPoint) : null; if (_dataReceiver == null) { _dataReceiver = new SctpDataReceiver(ARwnd, _defaultMTU, cookie.RemoteTSN); } if (_dataSender == null) { _dataSender = new SctpDataSender(ID, this.SendChunk, _defaultMTU, cookie.TSN, cookie.RemoteARwnd); } InitRemoteProperties(cookie.RemoteTag, cookie.RemoteTSN, cookie.RemoteARwnd); var cookieAckChunk = new SctpChunk(SctpChunkType.COOKIE_ACK); SendChunk(cookieAckChunk); SetState(SctpAssociationState.Established); _dataSender.StartSending(); CancelTimers(); } }