diff --git a/skyscraper8/Ietf/FLUTE/FluteListener.cs b/skyscraper8/Ietf/FLUTE/FluteListener.cs index 1da3b8f..e830616 100644 --- a/skyscraper8/Ietf/FLUTE/FluteListener.cs +++ b/skyscraper8/Ietf/FLUTE/FluteListener.cs @@ -72,6 +72,9 @@ namespace skyscraper8.Ietf.FLUTE if (blocks == null) blocks = new Dictionary, FluteBlock>(); + if (lctFrame.FecHeader == null) + return; + ushort sbn = lctFrame.FecHeader.SourceBlockNumber; ushort esi = lctFrame.FecHeader.EncodingSymbolId; diff --git a/skyscraper8/Ietf/FLUTE/LctFrame.cs b/skyscraper8/Ietf/FLUTE/LctFrame.cs index 2d3c5b5..0ef7fde 100644 --- a/skyscraper8/Ietf/FLUTE/LctFrame.cs +++ b/skyscraper8/Ietf/FLUTE/LctFrame.cs @@ -1,9 +1,11 @@ using skyscraper5.Skyscraper.IO; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; +using skyscraper5.Skyscraper; namespace skyscraper8.Ietf.FLUTE { @@ -25,7 +27,10 @@ namespace skyscraper8.Ietf.FLUTE this.CloseObjectFlag = (byteB & 0x01) != 0; if (this.Version != 1) - throw new NotSupportedException(String.Format("FLUTE Version {0}", Version)); + { + Debug.WriteLine(String.Format("FLUTE Version {0}", Version)); + return; + } int HeaderLength = ms.ReadUInt8(); HeaderLength *= 4; @@ -35,6 +40,8 @@ namespace skyscraper8.Ietf.FLUTE byte[] headerBuffer = ms.ReadBytes(HeaderLength); this.LctHeader = new LctHeader(headerBuffer,CongestionControlFlag,TransportSessionIdentifierFlag,TransportObjectIdentifierFlag,HalfWordFlag); + if (!this.LctHeader.Valid) + return; if (ms.GetAvailableBytes() < 4) return; diff --git a/skyscraper8/Ietf/FLUTE/LctHeader.cs b/skyscraper8/Ietf/FLUTE/LctHeader.cs index c43f520..39d83cb 100644 --- a/skyscraper8/Ietf/FLUTE/LctHeader.cs +++ b/skyscraper8/Ietf/FLUTE/LctHeader.cs @@ -2,13 +2,15 @@ using skyscraper8.DvbNip; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Text; using System.Threading.Tasks; +using skyscraper5.Skyscraper; namespace skyscraper8.Ietf.FLUTE { - internal class LctHeader + internal class LctHeader : Validatable { /// /// @@ -32,6 +34,11 @@ namespace skyscraper8.Ietf.FLUTE CongestionControlInformation = ReadField(ms, cciLength); TransportSessionIdentifier = ReadField(ms, tsiLength); TransportObjectIdentifier = ReadField(ms, toiLength); + if (CongestionControlInformation == UInt64.MaxValue || TransportSessionIdentifier == UInt64.MaxValue || TransportObjectIdentifier == UInt64.MaxValue) + { + Valid = false; + return; + } while (ms.GetAvailableBytes() >= 1) { @@ -49,7 +56,9 @@ namespace skyscraper8.Ietf.FLUTE this.ContentEncoding = new ContentEncodingOfFdtInstance(fixedHeaderExtension); break; default: - throw new NotImplementedException(String.Format("LCT Header Extension {0}", extensionId)); + Debug.WriteLine(String.Format("LCT Header Extension {0}", extensionId)); + Valid = false; + return; } } else @@ -58,6 +67,11 @@ namespace skyscraper8.Ietf.FLUTE headerExtensionLength *= 4; if (headerExtensionLength >= 2) headerExtensionLength -= 2; + if (headerExtensionLength > ms.GetAvailableBytes()) + { + Valid = false; + return; + } byte[] extensionBuffer = ms.ReadBytes(headerExtensionLength); switch(extensionId) { @@ -73,11 +87,15 @@ namespace skyscraper8.Ietf.FLUTE this.NipActualCarrierInformation = new NipActualCarrierInformation(extensionBuffer); break; default: - throw new NotImplementedException(String.Format("LCT Header Extension {0}", extensionId)); + Debug.WriteLine(String.Format("LCT Header Extension {0}", extensionId)); + Valid = false; + return; } } } + + Valid = true; } private ulong ReadField(Stream stream, int bits) @@ -93,7 +111,7 @@ namespace skyscraper8.Ietf.FLUTE case 64: return stream.ReadUInt64BE(); default: - throw new NotImplementedException(String.Format("{0} bits.", bits)); + return UInt64.MaxValue; } } public ulong CongestionControlInformation { get; private set; } diff --git a/skyscraper8/Program.cs b/skyscraper8/Program.cs index 6963cde..e827496 100644 --- a/skyscraper8/Program.cs +++ b/skyscraper8/Program.cs @@ -40,7 +40,7 @@ namespace skyscraper5 class Program { private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name); - private const int PUBLIC_RELEASE = 8; + private const int PUBLIC_RELEASE = 9; private static void IntegrationTest() { /*List ssdpDevices = SsdpClient.GetSsdpDevices(1000).ToList(); diff --git a/skyscraper8/Properties/launchSettings.json b/skyscraper8/Properties/launchSettings.json index 6ebaf7b..df6935c 100644 --- a/skyscraper8/Properties/launchSettings.json +++ b/skyscraper8/Properties/launchSettings.json @@ -2,7 +2,7 @@ "profiles": { "skyscraper8": { "commandName": "Project", - "commandLineArgs": "\"C:\\devel\\skyscraper8\\skyscraper8\\bin\\Debug\\net8.0\\638938290099956387.ts\"", + "commandLineArgs": "satip auto 1 H 11141 S2 23500", "remoteDebugEnabled": false }, "Container (Dockerfile)": { diff --git a/skyscraper8/QuickAndDirtySatIpClient.cs b/skyscraper8/QuickAndDirtySatIpClient.cs index 80af382..8a84ef9 100644 --- a/skyscraper8/QuickAndDirtySatIpClient.cs +++ b/skyscraper8/QuickAndDirtySatIpClient.cs @@ -20,7 +20,7 @@ namespace skyscraper8 internal class QuickAndDirtySatIpClient { private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name); - private const bool ENABLE_TS_WRITER = true; + private const bool ENABLE_TS_WRITER = false; public QuickAndDirtySatIpClient(string[] args) { @@ -106,6 +106,11 @@ namespace skyscraper8 packetQueue = new Queue(); RtspSetupResponse setup = rtspClient.GetSetup(url); + if (setup.RtspStatusCode == 404) + { + logger.Fatal("Your SAT>IP server doesn't have a tuner available that can talk to the requested frequency."); + return; + } setup.OnRtcpPacket += Setup_OnRtcpPacket; setup.OnRtpPacket += Setup_OnRtpPacket; diff --git a/skyscraper8/Skyscraper/BbframeDeencapsulator.cs b/skyscraper8/Skyscraper/BbframeDeencapsulator.cs index 595b1f8..2bccc06 100644 --- a/skyscraper8/Skyscraper/BbframeDeencapsulator.cs +++ b/skyscraper8/Skyscraper/BbframeDeencapsulator.cs @@ -1,12 +1,14 @@ -using System; +using skyscraper5.Mpeg2; +using skyscraper5.Skyscraper; +using skyscraper5.Skyscraper.IO; +using System; using System.Collections.Generic; using System.Linq; using System.Net.NetworkInformation; +using System.Net.Sockets; +using System.Runtime.Serialization; using System.Text; using System.Threading.Tasks; -using skyscraper5.Mpeg2; -using skyscraper5.Skyscraper; -using skyscraper5.Skyscraper.IO; namespace skyscraper8.Skyscraper { @@ -15,6 +17,7 @@ namespace skyscraper8.Skyscraper private bool interruptedGseHem; private MemoryStream interruptedGseHemBuffer; private bool interruptedGseHemWantsCrc32; + public IGsEventHandler MpeEventHandler { get; set; } public void PushPacket(byte[] bbframe) { @@ -49,37 +52,202 @@ namespace skyscraper8.Skyscraper } } - private bool isGseSynced; - private int gseNeeded; - private MemoryStream gseNeededBuffer; + private bool gseNeedMore; + private MemoryStream gseAssembler; + public int PID; + private void HandleGse(MemoryStream ms) { - if (!isGseSynced) + ms.Position = 10; + byte syncByte = ms.ReadUInt8(); + ms.Position = 10; + + long availableBytes = ms.GetAvailableBytes(); + byte[] readBytes = ms.ReadBytes(availableBytes); + + GseAssemblyState assemblyState = ValidateGse(gseAssembler); + if (assemblyState == GseAssemblyState.NOT_YET_BEGUN) { - ms.Position = 10; - byte syncByte = ms.ReadUInt8(); - ms.Position = 10; if ((syncByte & 0xc0) == 0xc0) { - isGseSynced = true; + //Aktuell ist der GSE Assembler nicht beschäftigt, aber wir haben ein Sync-Byte, also kann er loslegen. + gseAssembler = new MemoryStream(); + gseAssembler.Write(readBytes, 0, readBytes.Length); + return; } else { + //Aktuell ist der GSE Assembler nicht beschäftigt, und wir haben kein gültiges Sync-Byte, also weg damit. + //Console.WriteLine("gse not in sync yet. sync byte = {0}", syncByte); return; } } - if (gseNeeded > 0) + else if (assemblyState == GseAssemblyState.NEED_MORE_DATA) { - int blockSize = (int)System.Math.Min(gseNeeded, ms.GetAvailableBytes()); - gseNeededBuffer.Write(ms.ReadBytes(blockSize), 0, blockSize); - gseNeeded -= blockSize; - if (gseNeeded == 0) + gseAssembler.Write(readBytes, 0, readBytes.Length); + return; + } + else if (assemblyState == GseAssemblyState.VALID) + { + gseAssembler.Position = 0; + AssembleGse(gseAssembler); + //Console.WriteLine("assembled {0} next sync byte is {1}", sucessfulAssembles++,syncByte); + if (sucessfulAssembles == 181) { - int payloadSize = (int)gseNeededBuffer.Position; - gseNeededBuffer.Position = 0; - byte[] payloadBuffer = gseNeededBuffer.ReadBytes(payloadSize); + + } + gseAssembler = null; + HandleGse(ms); + return; + } + else if (assemblyState == GseAssemblyState.BROKEN_SUSPECT_ETHERTYPE) + { + gseAssembler = null; + HandleGse(ms); + return; + } + else if (assemblyState == GseAssemblyState.BROKEN_NEGATIVE_LENGTH) + { + gseAssembler = null; + HandleGse(ms); + return; + } + else + { + throw new NotImplementedException(String.Format("Unknown GSE assembler state: {0}, sync byte 0x{1}", assemblyState, syncByte)); + } + } + + private long sucessfulAssembles; + enum GseAssemblyState + { + NOT_YET_BEGUN, + VALID, + BROKEN_NEGATIVE_LENGTH, + NEED_MORE_DATA, + BROKEN_SUSPECT_LENGTH, + BROKEN_SUSPECT_ETHERTYPE + } + + private GseAssemblyState ValidateGse(MemoryStream ms) + { + if (ms == null) + return GseAssemblyState.NOT_YET_BEGUN; + + long backupPosition = ms.Position; + ms.Position = 0; + while (ms.GetAvailableBytes() > 2) + { + //GSE-Header + byte byteA = ms.ReadUInt8(); + bool startIndicator = (byteA & 0x80) != 0; + bool endIndicator = (byteA & 0x40) != 0; + int labelIndicator = (byteA & 0x30) >> 4; + if (!startIndicator && !endIndicator && labelIndicator == 0) + { + //end of base band frame + ms.Position = backupPosition; + return GseAssemblyState.VALID; + } + else + { + byte byteB = ms.ReadUInt8(); + int gseLength = byteA & 0x0f; + gseLength <<= 8; + gseLength += byteB; + //Console.WriteLine("GSE Length: {0}", gseLength); + if (gseLength > 9000) + { + ms.Position = backupPosition; + return GseAssemblyState.BROKEN_SUSPECT_LENGTH; + } + + if (!startIndicator || !endIndicator) + { + byte fragId = ms.ReadUInt8(); + gseLength--; + } + + if (startIndicator && !endIndicator) + { + ushort totalLength = ms.ReadUInt16BE(); + gseLength -= 2; + } + + if (startIndicator) + { + if (2 > ms.GetAvailableBytes()) + { + ms.Position = backupPosition; + return GseAssemblyState.NEED_MORE_DATA; + } + ushort protocolType = ms.ReadUInt16BE(); + if (!ValidateEthertype(protocolType)) + { + ms.Position = backupPosition; + return GseAssemblyState.BROKEN_SUSPECT_ETHERTYPE; + } + gseLength -= 2; + if (labelIndicator == 0) + { + if (6 > ms.GetAvailableBytes()) + { + ms.Position = backupPosition; + return GseAssemblyState.NEED_MORE_DATA; + } + PhysicalAddress sixByteLabel = new PhysicalAddress(ms.ReadBytes(6)); + gseLength -= 6; + } + else if (labelIndicator == 1) + { + byte[] threeByteLabel = ms.ReadBytes(3); + gseLength -= 3; + } + } + + if (!startIndicator && endIndicator) + gseLength -= 4; + + int startCrc32 = (int)ms.Position; + + if (gseLength < 0) + { + ms.Position = backupPosition; + return GseAssemblyState.BROKEN_NEGATIVE_LENGTH; + } + + if (gseLength > ms.GetAvailableBytes()) + { + ms.Position = backupPosition; + return GseAssemblyState.NEED_MORE_DATA; + } + + ms.Position += gseLength; + if (!startIndicator && endIndicator) + { + uint crc32 = ms.ReadUInt32BE(); + int endCrc32 = (int)ms.Position; + DvbCrc32.ValidateCrc(ms, startCrc32, endCrc32); + } } } + + ms.Position = backupPosition; + return GseAssemblyState.VALID; + } + + private bool ValidateEthertype(ushort ethertype) + { + if (ethertype == 0x0800) + return true; //IPv4 + if (ethertype == 0x86DD) + return true; //IPv6 + else + return false; //There are many more valid values, but since we don't speak anything aside from IPv4/IPv6, we discard it. + } + private void AssembleGse(MemoryStream ms) + { + ushort protocolType = 0; while (ms.GetAvailableBytes() > 2) { //GSE-Header @@ -98,7 +266,7 @@ namespace skyscraper8.Skyscraper int gseLength = byteA & 0x0f; gseLength <<= 8; gseLength += byteB; - + if (!startIndicator || !endIndicator) { byte fragId = ms.ReadUInt8(); @@ -113,7 +281,7 @@ namespace skyscraper8.Skyscraper if (startIndicator) { - ushort protocolType = ms.ReadUInt16BE(); + protocolType = ms.ReadUInt16BE(); gseLength -= 2; if (labelIndicator == 0) { @@ -131,15 +299,7 @@ namespace skyscraper8.Skyscraper gseLength -= 4; int startCrc32 = (int)ms.Position; - - if (gseLength > ms.GetAvailableBytes()) - { - gseNeeded = gseLength; - gseNeededBuffer = new MemoryStream(); - gseNeeded -= (int)ms.GetAvailableBytes(); - gseNeededBuffer.Write(ms.ReadBytes(ms.GetAvailableBytes())); - return; - } + byte[] payload = ms.ReadBytes(gseLength); if (!startIndicator && endIndicator) { @@ -148,9 +308,19 @@ namespace skyscraper8.Skyscraper DvbCrc32.ValidateCrc(ms, startCrc32, endCrc32); } - + switch (protocolType) + { + case 0x0800: //IPv4 + MpeEventHandler.OnIpDatagram(PID, payload); + break; + default: + //throw new NotImplementedException(String.Format("Unknown GSE Protocol ID 0x{0:X4}", protocolType)); + break; + } } } } + + } } diff --git a/skyscraper8/Skyscraper/DigitalDevicesBbFrameReader.cs b/skyscraper8/Skyscraper/DigitalDevicesBbFrameReader.cs index 03872c8..7787b96 100644 --- a/skyscraper8/Skyscraper/DigitalDevicesBbFrameReader.cs +++ b/skyscraper8/Skyscraper/DigitalDevicesBbFrameReader.cs @@ -40,36 +40,13 @@ namespace skyscraper5.src.Skyscraper { byte[] chi = outbuf.ToArray(); if (deencapsulator == null) + { deencapsulator = new BbframeDeencapsulator(); + deencapsulator.MpeEventHandler = mpeEventHandler; + deencapsulator.PID = pid; + } + deencapsulator.PushPacket(chi); - /*List ipPackets = IpPacketFinder.LookForIpPackets(outbuf.ToArray()); - if (ipPackets != null) - { - foreach (byte[] ipPacket in ipPackets) - { - mpeEventHandler.OnIpDatagram(0x118, ipPacket); - packetsRecovered++; - } - } - else - { - packetsLost++; - }*/ - /*byte[] ipPacket = IpPacketFinder.LookForIpPacket(outbuf.ToArray(), 32); - if (ipPacket != null) - { - if (!annoncementDone) - { - mpeEventHandler.GsIpTrafficDetected(); - annoncementDone = true; - } - mpeEventHandler.OnIpDatagram(0x0118, ipPacket); - packetsRecovered++; - } - else - { - packetsLost++; - }*/ } outbuf = new MemoryStream(); outbuf.Write(packets,9, packets[7] - 1);