示例#1
0
        protected void GotInit(SctpPacket initPacket, IPEndPoint remoteEndPoint)
        {
            // INIT packets have specific processing rules in order to prevent resource exhaustion.
            // See Section 5 of RFC 4960 https://tools.ietf.org/html/rfc4960#section-5 "Association Initialization".

            SctpInitChunk initChunk = initPacket.Chunks.Single(x => x.KnownType == SctpChunkType.INIT) as SctpInitChunk;

            if (initChunk.InitiateTag == 0 ||
                initChunk.NumberInboundStreams == 0 ||
                initChunk.NumberOutboundStreams == 0)
            {
                // If the value of the Initiate Tag in a received INIT chunk is found
                // to be 0, the receiver MUST treat it as an error and close the
                // association by transmitting an ABORT. (RFC4960 pg. 25)

                // Note: A receiver of an INIT with the OS value set to 0 SHOULD
                // abort the association. (RFC4960 pg. 25)

                // Note: A receiver of an INIT with the MIS value of 0 SHOULD abort
                // the association. (RFC4960 pg. 26)

                SendError(
                    true,
                    initPacket.Header.DestinationPort,
                    initPacket.Header.SourcePort,
                    initChunk.InitiateTag,
                    new SctpCauseOnlyError(SctpErrorCauseCode.InvalidMandatoryParameter));
            }
            else
            {
                var initAckPacket = GetInitAck(initPacket, remoteEndPoint);
                var buffer        = initAckPacket.GetBytes();
                Send(null, buffer, 0, buffer.Length);
            }
        }
示例#2
0
        /// <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>
        /// Creates the INIT ACK chunk and packet to send as a response to an SCTP
        /// packet containing an INIT chunk.
        /// </summary>
        /// <param name="initPacket">The received packet containing the INIT chunk.</param>
        /// <param name="remoteEP">Optional. The remote IP end point the INIT packet was
        /// received on. For transports that don't use an IP transport directly this parameter
        /// can be set to null and it will not form part of the COOKIE ECHO checks.</param>
        /// <returns>An SCTP packet with a single INIT ACK chunk.</returns>
        protected SctpPacket GetInitAck(SctpPacket initPacket, IPEndPoint remoteEP)
        {
            SctpInitChunk initChunk = initPacket.Chunks.Single(x => x.KnownType == SctpChunkType.INIT) as SctpInitChunk;

            SctpPacket initAckPacket = new SctpPacket(
                initPacket.Header.DestinationPort,
                initPacket.Header.SourcePort,
                initChunk.InitiateTag);

            var cookie = GetInitAckCookie(
                initPacket.Header.DestinationPort,
                initPacket.Header.SourcePort,
                initChunk.InitiateTag,
                initChunk.InitialTSN,
                initChunk.ARwnd,
                remoteEP != null ? remoteEP.ToString() : string.Empty,
                (int)(initChunk.CookiePreservative / 1000));

            var json       = cookie.ToJson();
            var jsonBuffer = Encoding.UTF8.GetBytes(json);

            using (HMACSHA256 hmac = new HMACSHA256(_hmacKey))
            {
                var result = hmac.ComputeHash(jsonBuffer);
                cookie.HMAC = result.HexStr();
            }

            var jsonWithHMAC       = cookie.ToJson();
            var jsonBufferWithHMAC = Encoding.UTF8.GetBytes(jsonWithHMAC);

            SctpInitChunk initAckChunk = new SctpInitChunk(
                SctpChunkType.INIT_ACK,
                cookie.Tag,
                cookie.TSN,
                cookie.ARwnd,
                SctpAssociation.DEFAULT_NUMBER_OUTBOUND_STREAMS,
                SctpAssociation.DEFAULT_NUMBER_INBOUND_STREAMS);

            initAckChunk.StateCookie = jsonBufferWithHMAC;
            initAckChunk.UnrecognizedPeerParameters = initChunk.UnrecognizedPeerParameters;

            initAckPacket.AddChunk(initAckChunk);

            return(initAckPacket);
        }
示例#4
0
        /// <summary>
        /// Attempts to create an association with a remote party by sending an initialisation
        /// control chunk.
        /// </summary>
        private void SendInit()
        {
            if (!_wasAborted)
            {
                // A packet containing an INIT chunk MUST have a zero Verification Tag (RFC4960 Pg 15).
                SctpPacket init = new SctpPacket(_sctpSourcePort, _sctpDestinationPort, 0);

                SctpInitChunk initChunk = new SctpInitChunk(
                    SctpChunkType.INIT,
                    VerificationTag,
                    TSN,
                    ARwnd,
                    _numberOutboundStreams,
                    _numberInboundStreams);
                init.AddChunk(initChunk);

                SetState(SctpAssociationState.CookieWait);

                byte[] buffer = init.GetBytes();
                _sctpTransport.Send(ID, buffer, 0, buffer.Length);

                _t1Init = new Timer(T1InitTimerExpired, init, T1_INIT_TIMER_MILLISECONDS, T1_INIT_TIMER_MILLISECONDS);
            }
        }
示例#5
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);
        }