Пример #1
0
        /// <summary>
        /// Parses the an DCEP open message from a buffer.
        /// </summary>
        /// <param name="buffer">The buffer to parse the message from.</param>
        /// <param name="posn">The position in the buffer to start parsing from.</param>
        /// <returns>A new DCEP open message instance.</returns>
        public static DataChannelOpenMessage Parse(byte[] buffer, int posn)
        {
            if (buffer.Length < DCEP_OPEN_FIXED_PARAMETERS_LENGTH)
            {
                throw new ApplicationException("The buffer did not contain the minimum number of bytes for a DCEP open message.");
            }

            var dcepOpen = new DataChannelOpenMessage();

            dcepOpen.MessageType = buffer[posn];
            dcepOpen.ChannelType = buffer[posn + 1];
            dcepOpen.Priority    = NetConvert.ParseUInt16(buffer, posn + 2);
            dcepOpen.Reliability = NetConvert.ParseUInt32(buffer, posn + 4);

            ushort labelLength    = NetConvert.ParseUInt16(buffer, posn + 8);
            ushort protocolLength = NetConvert.ParseUInt16(buffer, posn + 10);

            if (labelLength > 0)
            {
                dcepOpen.Label = Encoding.UTF8.GetString(buffer, 12, labelLength);
            }

            if (protocolLength > 0)
            {
                dcepOpen.Protocol = Encoding.UTF8.GetString(buffer, 12 + labelLength, protocolLength);
            }

            return(dcepOpen);
        }
Пример #2
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);
        }
Пример #3
0
        /// <summary>
        /// Parses the SACK 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 SctpSackChunk ParseChunk(byte[] buffer, int posn)
        {
            var    sackChunk = new SctpSackChunk();
            ushort chunkLen  = sackChunk.ParseFirstWord(buffer, posn);

            ushort startPosn = (ushort)(posn + SCTP_CHUNK_HEADER_LENGTH);

            sackChunk.CumulativeTsnAck = NetConvert.ParseUInt32(buffer, startPosn);
            sackChunk.ARwnd            = NetConvert.ParseUInt32(buffer, startPosn + 4);
            ushort numGapAckBlocks  = NetConvert.ParseUInt16(buffer, startPosn + 8);
            ushort numDuplicateTSNs = NetConvert.ParseUInt16(buffer, startPosn + 10);

            int reportPosn = startPosn + FIXED_PARAMETERS_LENGTH;

            for (int i = 0; i < numGapAckBlocks; i++)
            {
                ushort start = NetConvert.ParseUInt16(buffer, reportPosn);
                ushort end   = NetConvert.ParseUInt16(buffer, reportPosn + 2);
                sackChunk.GapAckBlocks.Add(new SctpTsnGapBlock {
                    Start = start, End = end
                });
                reportPosn += GAP_REPORT_LENGTH;
            }

            for (int j = 0; j < numDuplicateTSNs; j++)
            {
                sackChunk.DuplicateTSN.Add(NetConvert.ParseUInt32(buffer, reportPosn));
                reportPosn += DUPLICATE_TSN_LENGTH;
            }

            return(sackChunk);
        }
Пример #4
0
        /// <summary>
        /// The first 32 bits of all chunk parameters represent the type and length. This method
        /// parses those fields and sets them on the current instance.
        /// </summary>
        /// <param name="buffer">The buffer holding the serialised chunk parameter.</param>
        /// <param name="posn">The position in the buffer that indicates the start of the chunk parameter.</param>
        public ushort ParseFirstWord(byte[] buffer, int posn)
        {
            ParameterType = NetConvert.ParseUInt16(buffer, posn);
            ushort paramLen = NetConvert.ParseUInt16(buffer, posn + 2);

            if (paramLen > 0 && buffer.Length < posn + paramLen)
            {
                // The buffer was not big enough to supply the specified chunk parameter.
                int bytesRequired  = paramLen;
                int bytesAvailable = buffer.Length - posn;
                throw new ApplicationException($"The SCTP chunk parameter buffer was too short. " +
                                               $"Required {bytesRequired} bytes but only {bytesAvailable} available.");
            }

            return(paramLen);
        }
Пример #5
0
        /// <summary>
        /// Parses the an SCTP header from a buffer.
        /// </summary>
        /// <param name="buffer">The buffer to parse the SCTP header from.</param>
        /// <param name="posn">The position in the buffer to start parsing the header from.</param>
        /// <returns>A new SCTPHeaer instance.</returns>
        public static SctpHeader Parse(byte[] buffer, int posn)
        {
            if (buffer.Length < SCTP_HEADER_LENGTH)
            {
                throw new ApplicationException("The buffer did not contain the minimum number of bytes for an SCTP header.");
            }

            SctpHeader header = new SctpHeader();

            header.SourcePort      = NetConvert.ParseUInt16(buffer, posn);
            header.DestinationPort = NetConvert.ParseUInt16(buffer, posn + 2);
            header.VerificationTag = NetConvert.ParseUInt32(buffer, posn + 4);
            header.Checksum        = NetConvert.ParseUInt32(buffer, posn + 8);

            return(header);
        }
Пример #6
0
        /// <summary>
        /// The first 32 bits of all chunks represent the same 3 fields. This method
        /// parses those fields and sets them on the current instance.
        /// </summary>
        /// <param name="buffer">The buffer holding the serialised chunk.</param>
        /// <param name="posn">The position in the buffer that indicates the start of the chunk.</param>
        /// <returns>The chunk length value.</returns>
        public ushort ParseFirstWord(byte[] buffer, int posn)
        {
            ChunkType  = buffer[posn];
            ChunkFlags = buffer[posn + 1];
            ushort chunkLength = NetConvert.ParseUInt16(buffer, posn + 2);

            if (chunkLength > 0 && buffer.Length < posn + chunkLength)
            {
                // The buffer was not big enough to supply the specified chunk length.
                int bytesRequired  = chunkLength;
                int bytesAvailable = buffer.Length - posn;
                throw new ApplicationException($"The SCTP chunk buffer was too short. Required {bytesRequired} bytes but only {bytesAvailable} available.");
            }

            return(chunkLength);
        }
Пример #7
0
        /// <summary>
        /// Parses the INIT or INIT ACK 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 SctpInitChunk ParseChunk(byte[] buffer, int posn)
        {
            var    initChunk = new SctpInitChunk();
            ushort chunkLen  = initChunk.ParseFirstWord(buffer, posn);

            int startPosn = posn + SCTP_CHUNK_HEADER_LENGTH;

            initChunk.InitiateTag           = NetConvert.ParseUInt32(buffer, startPosn);
            initChunk.ARwnd                 = NetConvert.ParseUInt32(buffer, startPosn + 4);
            initChunk.NumberOutboundStreams = NetConvert.ParseUInt16(buffer, startPosn + 8);
            initChunk.NumberInboundStreams  = NetConvert.ParseUInt16(buffer, startPosn + 10);
            initChunk.InitialTSN            = NetConvert.ParseUInt32(buffer, startPosn + 12);

            int paramPosn          = startPosn + FIXED_PARAMETERS_LENGTH;
            int paramsBufferLength = chunkLen - SCTP_CHUNK_HEADER_LENGTH - FIXED_PARAMETERS_LENGTH;

            if (paramPosn < paramsBufferLength)
            {
                bool stopProcessing = false;

                foreach (var varParam in GetParameters(buffer, paramPosn, paramsBufferLength))
                {
                    switch (varParam.ParameterType)
                    {
                    case (ushort)SctpInitChunkParameterType.IPv4Address:
                    case (ushort)SctpInitChunkParameterType.IPv6Address:
                        var address = new IPAddress(varParam.ParameterValue);
                        initChunk.Addresses.Add(address);
                        break;

                    case (ushort)SctpInitChunkParameterType.CookiePreservative:
                        initChunk.CookiePreservative = NetConvert.ParseUInt32(varParam.ParameterValue, 0);
                        break;

                    case (ushort)SctpInitChunkParameterType.HostNameAddress:
                        initChunk.HostnameAddress = Encoding.UTF8.GetString(varParam.ParameterValue);
                        break;

                    case (ushort)SctpInitChunkParameterType.SupportedAddressTypes:
                        for (int valPosn = 0; valPosn < varParam.ParameterValue.Length; valPosn += 2)
                        {
                            switch (NetConvert.ParseUInt16(varParam.ParameterValue, valPosn))
                            {
                            case (ushort)SctpInitChunkParameterType.IPv4Address:
                                initChunk.SupportedAddressTypes.Add(SctpInitChunkParameterType.IPv4Address);
                                break;

                            case (ushort)SctpInitChunkParameterType.IPv6Address:
                                initChunk.SupportedAddressTypes.Add(SctpInitChunkParameterType.IPv6Address);
                                break;

                            case (ushort)SctpInitChunkParameterType.HostNameAddress:
                                initChunk.SupportedAddressTypes.Add(SctpInitChunkParameterType.HostNameAddress);
                                break;
                            }
                        }
                        break;

                    case (ushort)SctpInitChunkParameterType.EcnCapable:
                        break;

                    case (ushort)SctpInitChunkParameterType.StateCookie:
                        // Used with INIT ACK chunks only.
                        initChunk.StateCookie = varParam.ParameterValue;
                        break;

                    case (ushort)SctpInitChunkParameterType.UnrecognizedParameter:
                        // Used with INIT ACK chunks only. This parameter is the remote peer returning
                        // a list of parameters it did not understand in the INIT chunk.
                        initChunk.UnrecognizedParameters.Add(varParam.ParameterValue);
                        break;

                    default:
                        // Parameters are not recognised in an INIT or INIT ACK.
                        initChunk.GotUnrecognisedParameter(varParam);
                        break;
                    }

                    if (stopProcessing)
                    {
                        logger.LogWarning($"SCTP unrecognised parameter {varParam.ParameterType} for chunk type {initChunk.KnownType} "
                                          + "indicated no further chunks should be processed.");
                        break;
                    }
                }
            }

            return(initChunk);
        }
Пример #8
0
        /// <summary>
        /// Parses the ERROR 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 SctpErrorChunk ParseChunk(byte[] buffer, int posn, bool isAbort)
        {
            var    errorChunk = (isAbort) ? new SctpAbortChunk(false) : new SctpErrorChunk();
            ushort chunkLen   = errorChunk.ParseFirstWord(buffer, posn);

            int paramPosn          = posn + SCTP_CHUNK_HEADER_LENGTH;
            int paramsBufferLength = chunkLen - SCTP_CHUNK_HEADER_LENGTH;

            if (paramPosn < paramsBufferLength)
            {
                bool stopProcessing = false;

                foreach (var varParam in GetParameters(buffer, paramPosn, paramsBufferLength))
                {
                    switch (varParam.ParameterType)
                    {
                    case (ushort)SctpErrorCauseCode.InvalidStreamIdentifier:
                        ushort streamID = (ushort)((varParam.ParameterValue != null) ?
                                                   NetConvert.ParseUInt16(varParam.ParameterValue, 0) : 0);
                        var invalidStreamID = new SctpErrorInvalidStreamIdentifier {
                            StreamID = streamID
                        };
                        errorChunk.AddErrorCause(invalidStreamID);
                        break;

                    case (ushort)SctpErrorCauseCode.MissingMandatoryParameter:
                        List <ushort> missingIDs = new List <ushort>();
                        if (varParam.ParameterValue != null)
                        {
                            for (int i = 0; i < varParam.ParameterValue.Length; i += 2)
                            {
                                missingIDs.Add(NetConvert.ParseUInt16(varParam.ParameterValue, i));
                            }
                        }
                        var missingMandatory = new SctpErrorMissingMandatoryParameter {
                            MissingParameters = missingIDs
                        };
                        errorChunk.AddErrorCause(missingMandatory);
                        break;

                    case (ushort)SctpErrorCauseCode.StaleCookieError:
                        uint staleness = (uint)((varParam.ParameterValue != null) ?
                                                NetConvert.ParseUInt32(varParam.ParameterValue, 0) : 0);
                        var staleCookie = new SctpErrorStaleCookieError {
                            MeasureOfStaleness = staleness
                        };
                        errorChunk.AddErrorCause(staleCookie);
                        break;

                    case (ushort)SctpErrorCauseCode.OutOfResource:
                        errorChunk.AddErrorCause(new SctpCauseOnlyError(SctpErrorCauseCode.OutOfResource));
                        break;

                    case (ushort)SctpErrorCauseCode.UnresolvableAddress:
                        var unresolvable = new SctpErrorUnresolvableAddress {
                            UnresolvableAddress = varParam.ParameterValue
                        };
                        errorChunk.AddErrorCause(unresolvable);
                        break;

                    case (ushort)SctpErrorCauseCode.UnrecognizedChunkType:
                        var unrecognised = new SctpErrorUnrecognizedChunkType {
                            UnrecognizedChunk = varParam.ParameterValue
                        };
                        errorChunk.AddErrorCause(unrecognised);
                        break;

                    case (ushort)SctpErrorCauseCode.InvalidMandatoryParameter:
                        errorChunk.AddErrorCause(new SctpCauseOnlyError(SctpErrorCauseCode.InvalidMandatoryParameter));
                        break;

                    case (ushort)SctpErrorCauseCode.UnrecognizedParameters:
                        var unrecognisedParams = new SctpErrorUnrecognizedParameters {
                            UnrecognizedParameters = varParam.ParameterValue
                        };
                        errorChunk.AddErrorCause(unrecognisedParams);
                        break;

                    case (ushort)SctpErrorCauseCode.NoUserData:
                        uint tsn = (uint)((varParam.ParameterValue != null) ?
                                          NetConvert.ParseUInt32(varParam.ParameterValue, 0) : 0);
                        var noData = new SctpErrorNoUserData {
                            TSN = tsn
                        };
                        errorChunk.AddErrorCause(noData);
                        break;

                    case (ushort)SctpErrorCauseCode.CookieReceivedWhileShuttingDown:
                        errorChunk.AddErrorCause(new SctpCauseOnlyError(SctpErrorCauseCode.CookieReceivedWhileShuttingDown));
                        break;

                    case (ushort)SctpErrorCauseCode.RestartAssociationWithNewAddress:
                        var restartAddress = new SctpErrorRestartAssociationWithNewAddress
                        {
                            NewAddressTLVs = varParam.ParameterValue
                        };
                        errorChunk.AddErrorCause(restartAddress);
                        break;

                    case (ushort)SctpErrorCauseCode.UserInitiatedAbort:
                        string reason = (varParam.ParameterValue != null) ?
                                        Encoding.UTF8.GetString(varParam.ParameterValue) : null;
                        var userAbort = new SctpErrorUserInitiatedAbort {
                            AbortReason = reason
                        };
                        errorChunk.AddErrorCause(userAbort);
                        break;

                    case (ushort)SctpErrorCauseCode.ProtocolViolation:
                        string info = (varParam.ParameterValue != null) ?
                                      Encoding.UTF8.GetString(varParam.ParameterValue) : null;
                        var protocolViolation = new SctpErrorProtocolViolation {
                            AdditionalInformation = info
                        };
                        errorChunk.AddErrorCause(protocolViolation);
                        break;

                    default:
                        // Parameter was not recognised.
                        errorChunk.GotUnrecognisedParameter(varParam);
                        break;
                    }

                    if (stopProcessing)
                    {
                        logger.LogWarning($"SCTP unrecognised parameter {varParam.ParameterType} for chunk type {SctpChunkType.ERROR} "
                                          + "indicated no further chunks should be processed.");
                        break;
                    }
                }
            }

            return(errorChunk);
        }
Пример #9
0
        public static List <STUNAttribute> ParseMessageAttributes(byte[] buffer, int startIndex, int endIndex)
        {
            if (buffer != null && buffer.Length > startIndex && buffer.Length >= endIndex)
            {
                List <STUNAttribute> attributes = new List <STUNAttribute>();
                int startAttIndex = startIndex;

                while (startAttIndex < endIndex)
                {
                    UInt16 stunAttributeType   = NetConvert.ParseUInt16(buffer, startAttIndex);
                    UInt16 stunAttributeLength = NetConvert.ParseUInt16(buffer, startAttIndex + 2);
                    byte[] stunAttributeValue  = null;

                    if (stunAttributeLength > 0)
                    {
                        if (stunAttributeLength + startIndex + 4 > endIndex)
                        {
                            logger.LogWarning("The attribute length on a STUN parameter was greater than the available number of bytes.");
                        }
                        else
                        {
                            stunAttributeValue = new byte[stunAttributeLength];
                            Buffer.BlockCopy(buffer, startAttIndex + 4, stunAttributeValue, 0, stunAttributeLength);
                        }
                    }

                    STUNAttributeTypesEnum attributeType = STUNAttributeTypes.GetSTUNAttributeTypeForId(stunAttributeType);

                    STUNAttribute attribute = null;
                    if (attributeType == STUNAttributeTypesEnum.ChangeRequest)
                    {
                        attribute = new STUNChangeRequestAttribute(stunAttributeValue);
                    }
                    else if (attributeType == STUNAttributeTypesEnum.MappedAddress)
                    {
                        attribute = new STUNAddressAttribute(stunAttributeValue);
                    }
                    else if (attributeType == STUNAttributeTypesEnum.ErrorCode)
                    {
                        attribute = new STUNErrorCodeAttribute(stunAttributeValue);
                    }
                    else if (attributeType == STUNAttributeTypesEnum.XORMappedAddress || attributeType == STUNAttributeTypesEnum.XORPeerAddress || attributeType == STUNAttributeTypesEnum.XORRelayedAddress)
                    {
                        attribute = new STUNXORAddressAttribute(attributeType, stunAttributeValue);
                    }
                    else
                    {
                        attribute = new STUNAttribute(attributeType, stunAttributeValue);
                    }

                    attributes.Add(attribute);

                    // Attributes start on 32 bit word boundaries so where an attribute length is not a multiple of 4 it gets padded.
                    int padding = (stunAttributeLength % 4 != 0) ? 4 - (stunAttributeLength % 4) : 0;

                    startAttIndex = startAttIndex + 4 + stunAttributeLength + padding;
                }

                return(attributes);
            }
            else
            {
                return(null);
            }
        }
Пример #10
0
        /// <summary>
        /// Extracts the padded length field from a serialised chunk buffer.
        /// </summary>
        /// <param name="buffer">The buffer holding the serialised chunk.</param>
        /// <param name="posn">The start position of the serialised chunk.</param>
        /// <param name="padded">If true the length field will be padded to a 4 byte boundary.</param>
        /// <returns>The padded length of the serialised chunk.</returns>
        public static uint GetChunkLengthFromHeader(byte[] buffer, int posn, bool padded)
        {
            ushort len = NetConvert.ParseUInt16(buffer, posn + 2);

            return((padded) ? SctpPadding.PadTo4ByteBoundary(len) : len);
        }