コード例 #1
0
        /// <summary>
        /// Parses the DATA chunk fields
        /// </summary>
        /// <param name="buffer">The buffer holding the serialised chunk.</param>
        /// <param name="posn">The position to start parsing at.</param>
        public static SctpDataChunk ParseChunk(byte[] buffer, int posn)
        {
            var    dataChunk = new SctpDataChunk();
            ushort chunkLen  = dataChunk.ParseFirstWord(buffer, posn);

            if (chunkLen < FIXED_PARAMETERS_LENGTH)
            {
                throw new ApplicationException($"SCTP data chunk cannot be parsed as buffer too short for fixed parameter fields.");
            }

            dataChunk.Unordered = (dataChunk.ChunkFlags & 0x04) > 0;
            dataChunk.Begining  = (dataChunk.ChunkFlags & 0x02) > 0;
            dataChunk.Ending    = (dataChunk.ChunkFlags & 0x01) > 0;

            int startPosn = posn + SCTP_CHUNK_HEADER_LENGTH;

            dataChunk.TSN          = NetConvert.ParseUInt32(buffer, startPosn);
            dataChunk.StreamID     = NetConvert.ParseUInt16(buffer, startPosn + 4);
            dataChunk.StreamSeqNum = NetConvert.ParseUInt16(buffer, startPosn + 6);
            dataChunk.PPID         = NetConvert.ParseUInt32(buffer, startPosn + 8);

            int userDataPosn = startPosn + FIXED_PARAMETERS_LENGTH;
            int userDataLen  = chunkLen - SCTP_CHUNK_HEADER_LENGTH - FIXED_PARAMETERS_LENGTH;

            if (userDataLen > 0)
            {
                dataChunk.UserData = new byte[userDataLen];
                Buffer.BlockCopy(buffer, userDataPosn, dataChunk.UserData, 0, dataChunk.UserData.Length);
            }

            return(dataChunk);
        }
コード例 #2
0
ファイル: SctpChunk.cs プロジェクト: lucent-sea/sipsorcery
        /// <summary>
        /// Parses an SCTP chunk from a buffer.
        /// </summary>
        /// <param name="buffer">The buffer holding the serialised chunk.</param>
        /// <param name="posn">The position to start parsing at.</param>
        /// <returns>An SCTP chunk instance.</returns>
        public static SctpChunk Parse(byte[] buffer, int posn)
        {
            if (buffer.Length < posn + SCTP_CHUNK_HEADER_LENGTH)
            {
                throw new ApplicationException("Buffer did not contain the minimum of bytes for an SCTP chunk.");
            }

            byte chunkType = buffer[posn];

            if (Enum.IsDefined(typeof(SctpChunkType), chunkType))
            {
                switch ((SctpChunkType)chunkType)
                {
                case SctpChunkType.ABORT:
                    return(SctpAbortChunk.ParseChunk(buffer, posn, true));

                case SctpChunkType.DATA:
                    return(SctpDataChunk.ParseChunk(buffer, posn));

                case SctpChunkType.ERROR:
                    return(SctpErrorChunk.ParseChunk(buffer, posn, false));

                case SctpChunkType.SACK:
                    return(SctpSackChunk.ParseChunk(buffer, posn));

                case SctpChunkType.COOKIE_ACK:
                case SctpChunkType.COOKIE_ECHO:
                case SctpChunkType.HEARTBEAT:
                case SctpChunkType.HEARTBEAT_ACK:
                case SctpChunkType.SHUTDOWN_ACK:
                case SctpChunkType.SHUTDOWN_COMPLETE:
                    return(ParseBaseChunk(buffer, posn));

                case SctpChunkType.INIT:
                case SctpChunkType.INIT_ACK:
                    return(SctpInitChunk.ParseChunk(buffer, posn));

                case SctpChunkType.SHUTDOWN:
                    return(SctpShutdownChunk.ParseChunk(buffer, posn));

                default:
                    logger.LogDebug($"TODO: Implement parsing logic for well known chunk type {(SctpChunkType)chunkType}.");
                    return(ParseBaseChunk(buffer, posn));
                }
            }

            // Shouldn't reach this point. The SCTP packet parsing logic checks if the chunk is
            // recognised before attempting to parse it.
            throw new ApplicationException($"SCTP chunk type of {chunkType} was not recognised.");
        }
コード例 #3
0
        /// <summary>
        /// Sends a DATA chunk to the remote peer.
        /// </summary>
        /// <param name="streamID">The stream ID to sent the data on.</param>
        /// <param name="ppid">The payload protocol ID for the data.</param>
        /// <param name="message">The byte data to send.</param>
        public void SendData(ushort streamID, uint ppid, byte[] data)
        {
            lock (_sendQueue)
            {
                ushort seqnum = 0;

                if (_streamSeqnums.ContainsKey(streamID))
                {
                    unchecked
                    {
                        _streamSeqnums[streamID] = (ushort)(_streamSeqnums[streamID] + 1);
                        seqnum = _streamSeqnums[streamID];
                    }
                }
                else
                {
                    _streamSeqnums.Add(streamID, 0);
                }

                for (int index = 0; index *_defaultMTU < data.Length; index++)
                {
                    int offset        = (index == 0) ? 0 : (index * _defaultMTU);
                    int payloadLength = (offset + _defaultMTU < data.Length) ? _defaultMTU : data.Length - offset;

                    // Future TODO: Replace with slice when System.Memory is introduced as a dependency.
                    byte[] payload = new byte[payloadLength];
                    Buffer.BlockCopy(data, offset, payload, 0, payloadLength);

                    bool isBegining = index == 0;
                    bool isEnd      = ((offset + payloadLength) >= data.Length) ? true : false;

                    SctpDataChunk dataChunk = new SctpDataChunk(
                        false,
                        isBegining,
                        isEnd,
                        TSN,
                        streamID,
                        seqnum,
                        ppid,
                        payload);

                    _sendQueue.Enqueue(dataChunk);

                    TSN = (TSN == UInt32.MaxValue) ? 0 : TSN + 1;
                }

                _senderMre.Set();
            }
        }
コード例 #4
0
        /// <summary>
        /// Handler for processing new data chunks.
        /// </summary>
        /// <param name="dataChunk">The newly received data chunk.</param>
        /// <returns>If the received chunk resulted in a full chunk becoming available one
        /// or more new frames will be returned otherwise an empty frame is returned. Multiple
        /// frames may be returned if this chunk is part of a stream and was received out
        /// or order. For unordered chunks the list will always have a single entry.</returns>
        public List <SctpDataFrame> OnDataChunk(SctpDataChunk dataChunk)
        {
            var sortedFrames = new List <SctpDataFrame>();
            var frame        = SctpDataFrame.Empty;

            if (_inOrderReceiveCount == 0 &&
                GetDistance(_initialTSN, dataChunk.TSN) > _windowSize)
            {
                logger.LogWarning($"SCTP data receiver received a data chunk with a {dataChunk.TSN} " +
                                  $"TSN when the initial TSN was {_initialTSN} and a " +
                                  $"window size of {_windowSize}, ignoring.");
            }
            else if (_inOrderReceiveCount > 0 &&
                     GetDistance(_lastInOrderTSN, dataChunk.TSN) > _windowSize)
            {
                logger.LogWarning($"SCTP data receiver received a data chunk with a {dataChunk.TSN} " +
                                  $"TSN when the expected TSN was {_lastInOrderTSN + 1} and a " +
                                  $"window size of {_windowSize}, ignoring.");
            }
            else if (_inOrderReceiveCount > 0 &&
                     !IsNewer(_lastInOrderTSN, dataChunk.TSN))
            {
                logger.LogWarning($"SCTP data receiver received an old data chunk with {dataChunk.TSN} " +
                                  $"TSN when the expected TSN was {_lastInOrderTSN + 1}, ignoring.");
            }
            else if (!_forwardTSN.ContainsKey(dataChunk.TSN))
            {
                logger.LogTrace($"SCTP receiver got data chunk with TSN {dataChunk.TSN}, " +
                                $"last in order TSN {_lastInOrderTSN}, in order receive count {_inOrderReceiveCount}.");

                // Relying on unsigned integer wrapping.
                unchecked
                {
                    if ((_inOrderReceiveCount > 0 && _lastInOrderTSN + 1 == dataChunk.TSN) ||
                        (_inOrderReceiveCount == 0 && dataChunk.TSN == _initialTSN))
                    {
                        _inOrderReceiveCount++;
                        _lastInOrderTSN = dataChunk.TSN;

                        // See if the in order TSN can be bumped using any out of order chunks
                        // already received.
                        if (_inOrderReceiveCount > 0 && _forwardTSN.Count > 0)
                        {
                            while (_forwardTSN.ContainsKey(_lastInOrderTSN + 1))
                            {
                                _lastInOrderTSN++;
                                _inOrderReceiveCount++;
                                _forwardTSN.Remove(_lastInOrderTSN);
                            }
                        }
                    }
                    else
                    {
                        _forwardTSN.Add(dataChunk.TSN, 1);
                    }
                }

                // Now go about processing the data chunk.
                if (dataChunk.Begining && dataChunk.Ending)
                {
                    // Single packet chunk.
                    frame = new SctpDataFrame(
                        dataChunk.Unordered,
                        dataChunk.StreamID,
                        dataChunk.StreamSeqNum,
                        dataChunk.PPID,
                        dataChunk.UserData);
                }
                else
                {
                    // This is a data chunk fragment.
                    _fragmentedChunks.Add(dataChunk.TSN, dataChunk);
                    (var begin, var end) = GetChunkBeginAndEnd(_fragmentedChunks, dataChunk.TSN);

                    if (begin != null && end != null)
                    {
                        frame = GetFragmentedChunk(_fragmentedChunks, begin.Value, end.Value);
                    }
                }
            }
            else
            {
                logger.LogTrace($"SCTP duplicate TSN received for {dataChunk.TSN}.");
                if (!_duplicateTSN.ContainsKey(dataChunk.TSN))
                {
                    _duplicateTSN.Add(dataChunk.TSN, 1);
                }
                else
                {
                    _duplicateTSN[dataChunk.TSN] = _duplicateTSN[dataChunk.TSN] + 1;
                }
            }

            if (!frame.IsEmpty() && !dataChunk.Unordered)
            {
                return(ProcessStreamFrame(frame));
            }
            else
            {
                if (!frame.IsEmpty())
                {
                    sortedFrames.Add(frame);
                }

                return(sortedFrames);
            }
        }