/// <summary> /// Parses the SHUTDOWN 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 SctpShutdownChunk ParseChunk(byte[] buffer, int posn) { var shutdownChunk = new SctpShutdownChunk(); shutdownChunk.CumulativeTsnAck = NetConvert.ParseUInt32(buffer, posn + SCTP_CHUNK_HEADER_LENGTH); return(shutdownChunk); }
/// <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); }
/// <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); }
/// <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); }
/// <summary> /// Verifies whether the checksum for a serialised SCTP packet is valid. /// </summary> /// <param name="buffer">The buffer holding the serialised packet.</param> /// <param name="posn">The start position in the buffer.</param> /// <param name="length">The length of the packet in the buffer.</param> /// <returns>True if the checksum was valid, false if not.</returns> public static bool VerifyChecksum(byte[] buffer, int posn, int length) { uint origChecksum = NetConvert.ParseUInt32(buffer, posn + CHECKSUM_BUFFER_POSITION); NetConvert.ToBuffer(0U, buffer, posn + CHECKSUM_BUFFER_POSITION); uint calcChecksum = CRC32C.Calculate(buffer, posn, length); // Put the original checksum back. NetConvert.ToBuffer(origChecksum, buffer, posn + CHECKSUM_BUFFER_POSITION); return(origChecksum == NetConvert.EndianFlip(calcChecksum)); }
/// <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); }
public void ParseBindingRequestWithIceControlledAttribute() { var buffer = new byte[] { 0x00, 0x01, 0x00, 0x50, 0x21, 0x12, 0xa4, 0x42, 0x0c, 0x66, 0x64, 0x5a, 0xf7, 0xe9, 0xe6, 0x57, 0x3f, 0x53, 0x2b, 0x33, 0x00, 0x24, 0x00, 0x04, 0x6e, 0x7f, 0xff, 0xff, 0x80, 0x29, 0x00, 0x08, 0x27, 0xff, 0x2a, 0x17, 0x1b, 0x88, 0x8f, 0xfe, 0x80, 0x22, 0x00, 0x08, 0x6c, 0x69, 0x62, 0x6a, 0x75, 0x69, 0x63, 0x65, 0x00, 0x06, 0x00, 0x09, 0x4e, 0x4f, 0x43, 0x47, 0x3a, 0x6b, 0x57, 0x55, 0x48, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x14, 0x23, 0x7e, 0x24, 0x6c, 0x44, 0x12, 0xbc, 0x43, 0xfc, 0x63, 0x01, 0xf4, 0xda, 0x36, 0x73, 0xdf, 0x84, 0xb8, 0x23, 0xd6, 0x80, 0x28, 0x00, 0x04, 0x07, 0x8a, 0x49, 0x2e }; var stunReq = STUNMessage.ParseSTUNMessage(buffer, buffer.Length); Assert.NotNull(stunReq); Assert.Equal(1853882367U, NetConvert.ParseUInt32(stunReq.Attributes.Single(x => x.AttributeType == STUNAttributeTypesEnum.Priority).Value, 0)); Assert.Equal(8, stunReq.Attributes.Single(x => x.AttributeType == STUNAttributeTypesEnum.IceControlled).PaddedLength); Assert.Equal(0x27ff2a171b888ffeU, NetConvert.ParseUInt64(stunReq.Attributes.Single(x => x.AttributeType == STUNAttributeTypesEnum.IceControlled).Value, 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); }
/// <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); }
/// <summary> /// Gets the verification tag from a serialised SCTP packet. This allows /// a pre-flight check to be carried out before de-serialising the whole buffer. /// </summary> /// <param name="buffer">The buffer holding the serialised packet.</param> /// <param name="posn">The start position in the buffer.</param> /// <param name="length">The length of the packet in the buffer.</param> /// <returns>The verification tag for the serialised SCTP packet.</returns> public static uint GetVerificationTag(byte[] buffer, int posn, int length) { return(NetConvert.ParseUInt32(buffer, posn + VERIFICATIONTAG_BUFFER_POSITION)); }