From c999a9de4d202e2240f17cd3505710720866d91b Mon Sep 17 00:00:00 2001 From: feyris-tan <4116042+feyris-tan@users.noreply.github.com> Date: Tue, 6 Jan 2026 21:21:33 +0100 Subject: [PATCH] Almost solved the SES-12 puzzle. --- skyscraper8.Tests/IntegrationTests.cs | 7 +- .../ResourceTests/Ses12GseTest.cs | 19 ++ ...Frame00000357_TSGS1_MIS000_SYNC184.bbframe | Bin 0 -> 869 bytes skyscraper8.Tests/Resources1.Designer.cs | 154 +++++--------- skyscraper8.Tests/Resources1.resx | 5 +- skyscraper8.sln.DotSettings.user | 6 +- skyscraper8/GS/BBHeader.cs | 5 + skyscraper8/GS/BBframeDeencapsulator3.cs | 5 +- .../GseWithRollingSyncByteReader.cs | 185 +++++++++++++++++ skyscraper8/GS/GSE/GseReader.cs | 4 + skyscraper8/GS/GsContextDto.cs | 16 +- skyscraper8/GS/GsTypeDetector.cs | 109 ---------- skyscraper8/GS/MisHandlerProxy.cs | 190 ++++++++++++++++++ skyscraper8/GS/NullMisHandler.cs | 12 ++ skyscraper8/Id3/Id3ErrorState.cs | 3 +- skyscraper8/Id3/Id3PesProcessor.cs | 6 + .../InteractionChannelErrorState.cs | 2 +- .../InteractionChannelHandler.cs | 2 +- skyscraper8/InteractionChannel/Model/Tmst.cs | 2 +- skyscraper8/Mpeg2/Crc32.cs | 14 ++ skyscraper8/Program.cs | 5 +- 21 files changed, 524 insertions(+), 227 deletions(-) create mode 100644 skyscraper8.Tests/ResourceTests/Ses12GseTest.cs create mode 100644 skyscraper8.Tests/Resources/Frame00000357_TSGS1_MIS000_SYNC184.bbframe create mode 100644 skyscraper8/GS/GSE-RollingSyncByte/GseWithRollingSyncByteReader.cs delete mode 100644 skyscraper8/GS/GsTypeDetector.cs create mode 100644 skyscraper8/GS/MisHandlerProxy.cs create mode 100644 skyscraper8/GS/NullMisHandler.cs diff --git a/skyscraper8.Tests/IntegrationTests.cs b/skyscraper8.Tests/IntegrationTests.cs index a71c8f2..c9d595a 100644 --- a/skyscraper8.Tests/IntegrationTests.cs +++ b/skyscraper8.Tests/IntegrationTests.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using Microsoft.VisualStudio.TestTools.UnitTesting; using skyscraper5.Skyscraper; using skyscraper8.Skyscraper.Scraper.Storage.Tar; @@ -14,7 +15,9 @@ public class IntegrationTests string filename = String.Format("{0}.tar", DateTime.Now.ToUnixTime()); FileInfo fi = new FileInfo(filename); FileStream fileStream = fi.Open(FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.Read); - + fi.Refresh(); + Assert.IsTrue(fi.Exists); + Random rng = new Random(); TarArchive tar = new TarArchive(fileStream); @@ -29,4 +32,4 @@ public class IntegrationTests tar.WriteEntry(filename, buffer); } } -} +} \ No newline at end of file diff --git a/skyscraper8.Tests/ResourceTests/Ses12GseTest.cs b/skyscraper8.Tests/ResourceTests/Ses12GseTest.cs new file mode 100644 index 0000000..1b5c307 --- /dev/null +++ b/skyscraper8.Tests/ResourceTests/Ses12GseTest.cs @@ -0,0 +1,19 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using skyscraper8.GS; +using skyscraper8.GS.GSE_RollingSyncByte; +using skyscraper8.GSE; + +namespace skyscraper8.Tests.ResourceTests; + +[TestClass] +public class Ses12GseTest +{ + [TestMethod] + public void TestShortPdu() + { + byte[] datasource = Resources1.Frame00000357_TSGS1_MIS000_SYNC184; + GsContextDto dto = new GsContextDto(); + GseWithRollingSyncByteReader reader = new GseWithRollingSyncByteReader(dto); + reader.PushFrame(new BBHeader(new byte[10], 0), datasource); + } +} diff --git a/skyscraper8.Tests/Resources/Frame00000357_TSGS1_MIS000_SYNC184.bbframe b/skyscraper8.Tests/Resources/Frame00000357_TSGS1_MIS000_SYNC184.bbframe new file mode 100644 index 0000000000000000000000000000000000000000..c8c694dbc1b095c2459e352c324b51fe84018ff8 GIT binary patch literal 869 zcmYdzzL0@|fsF-7FtGT5NKSSz$qFXfKqPk&h~zN>kxbTLwh5Rt1C!=p(ilWCZv~Uv zK_sIzh-5DWk!)u`B=Z&!$?zIP@K%9Iur3}85SzgbMDWZ6k=&<2BqK!E4iK9GY&-W^ z5L1;wjX{V}kRg>}<@*=hJ9adj0Eq$x(jWpq8o=O;GAlCzfj)}p3t_Dk=MS}uJ01|ZFLa4!(7i{D=)B_9> t?c@eqD#OnH7lKoMz*Ag0!y)Mmz8g>hNU-$?HSj!PRDdS9lkd(X0suwTWZ?h+ literal 0 HcmV?d00001 diff --git a/skyscraper8.Tests/Resources1.Designer.cs b/skyscraper8.Tests/Resources1.Designer.cs index de7a55d..8910987 100644 --- a/skyscraper8.Tests/Resources1.Designer.cs +++ b/skyscraper8.Tests/Resources1.Designer.cs @@ -1,10 +1,9 @@ //------------------------------------------------------------------------------ // -// Dieser Code wurde von einem Tool generiert. -// Laufzeitversion:4.0.30319.42000 +// This code was generated by a tool. // -// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn -// der Code erneut generiert wird. +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. // //------------------------------------------------------------------------------ @@ -12,46 +11,32 @@ namespace skyscraper8.Tests { using System; - /// - /// Eine stark typisierte Ressourcenklasse zum Suchen von lokalisierten Zeichenfolgen usw. - /// - // Diese Klasse wurde von der StronglyTypedResourceBuilder automatisch generiert - // -Klasse über ein Tool wie ResGen oder Visual Studio automatisch generiert. - // Um einen Member hinzuzufügen oder zu entfernen, bearbeiten Sie die .ResX-Datei und führen dann ResGen - // mit der /str-Option erneut aus, oder Sie erstellen Ihr VS-Projekt neu. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] + [System.Diagnostics.DebuggerNonUserCodeAttribute()] + [System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources1 { - private static global::System.Resources.ResourceManager resourceMan; + private static System.Resources.ResourceManager resourceMan; - private static global::System.Globalization.CultureInfo resourceCulture; + private static System.Globalization.CultureInfo resourceCulture; - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + [System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal Resources1() { } - /// - /// Gibt die zwischengespeicherte ResourceManager-Instanz zurück, die von dieser Klasse verwendet wird. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + internal static System.Resources.ResourceManager ResourceManager { get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("skyscraper8.Tests.Resources1", typeof(Resources1).Assembly); + if (object.Equals(null, resourceMan)) { + System.Resources.ResourceManager temp = new System.Resources.ResourceManager("skyscraper8.Tests.Resources1", typeof(Resources1).Assembly); resourceMan = temp; } return resourceMan; } } - /// - /// Überschreibt die CurrentUICulture-Eigenschaft des aktuellen Threads für alle - /// Ressourcenzuordnungen, die diese stark typisierte Ressourcenklasse verwenden. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { + [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Advanced)] + internal static System.Globalization.CultureInfo Culture { get { return resourceCulture; } @@ -60,39 +45,6 @@ namespace skyscraper8.Tests { } } - /// - /// Sucht eine lokalisierte Ressource vom Typ System.Byte[]. - /// - internal static byte[] Frame00000008_TSGS1_MIS000_SYNC001 { - get { - object obj = ResourceManager.GetObject("Frame00000008_TSGS1_MIS000_SYNC001", resourceCulture); - return ((byte[])(obj)); - } - } - - /// - /// Sucht eine lokalisierte Ressource vom Typ System.Byte[]. - /// - internal static byte[] Frame00000012_TSGS1_MIS000_SYNC001 { - get { - object obj = ResourceManager.GetObject("Frame00000012_TSGS1_MIS000_SYNC001", resourceCulture); - return ((byte[])(obj)); - } - } - - /// - /// Sucht eine lokalisierte Ressource vom Typ System.Byte[]. - /// - internal static byte[] Frame00001343_TSGS1_MIS000_SYNC001 { - get { - object obj = ResourceManager.GetObject("Frame00001343_TSGS1_MIS000_SYNC001", resourceCulture); - return ((byte[])(obj)); - } - } - - /// - /// Sucht eine lokalisierte Ressource vom Typ System.Byte[]. - /// internal static byte[] ModemCapabilitiesEncodingTest { get { object obj = ResourceManager.GetObject("ModemCapabilitiesEncodingTest", resourceCulture); @@ -100,9 +52,6 @@ namespace skyscraper8.Tests { } } - /// - /// Sucht eine lokalisierte Ressource vom Typ System.Byte[]. - /// internal static byte[] MultipartRegistrationResponseTest { get { object obj = ResourceManager.GetObject("MultipartRegistrationResponseTest", resourceCulture); @@ -110,19 +59,6 @@ namespace skyscraper8.Tests { } } - /// - /// Sucht eine lokalisierte Ressource vom Typ System.Byte[]. - /// - internal static byte[] MultipartRegistrationResponseTest2 { - get { - object obj = ResourceManager.GetObject("MultipartRegistrationResponseTest2", resourceCulture); - return ((byte[])(obj)); - } - } - - /// - /// Sucht eine lokalisierte Ressource vom Typ System.Byte[]. - /// internal static byte[] PushMacManagementMessage_Version4_Type45 { get { object obj = ResourceManager.GetObject("PushMacManagementMessage_Version4_Type45", resourceCulture); @@ -130,9 +66,6 @@ namespace skyscraper8.Tests { } } - /// - /// Sucht eine lokalisierte Ressource vom Typ System.Byte[]. - /// internal static byte[] ranging_response_test { get { object obj = ResourceManager.GetObject("ranging_response_test", resourceCulture); @@ -140,19 +73,13 @@ namespace skyscraper8.Tests { } } - /// - /// Sucht eine lokalisierte Ressource vom Typ System.Byte[]. - /// - internal static byte[] sdpTest { + internal static byte[] MultipartRegistrationResponseTest2 { get { - object obj = ResourceManager.GetObject("sdpTest", resourceCulture); + object obj = ResourceManager.GetObject("MultipartRegistrationResponseTest2", resourceCulture); return ((byte[])(obj)); } } - /// - /// Sucht eine lokalisierte Ressource vom Typ System.Byte[]. - /// internal static byte[] test_1packet_01 { get { object obj = ResourceManager.GetObject("test-1packet-01", resourceCulture); @@ -160,9 +87,6 @@ namespace skyscraper8.Tests { } } - /// - /// Sucht eine lokalisierte Ressource vom Typ System.Byte[]. - /// internal static byte[] test_2packets_02_03 { get { object obj = ResourceManager.GetObject("test-2packets-02-03", resourceCulture); @@ -170,9 +94,6 @@ namespace skyscraper8.Tests { } } - /// - /// Sucht eine lokalisierte Ressource vom Typ System.Byte[]. - /// internal static byte[] test_3packets_04_05_06 { get { object obj = ResourceManager.GetObject("test-3packets-04-05-06", resourceCulture); @@ -180,9 +101,6 @@ namespace skyscraper8.Tests { } } - /// - /// Sucht eine lokalisierte Ressource vom Typ System.Byte[]. - /// internal static byte[] TransmitChannelConfigurationObject { get { object obj = ResourceManager.GetObject("TransmitChannelConfigurationObject", resourceCulture); @@ -190,14 +108,46 @@ namespace skyscraper8.Tests { } } - /// - /// Sucht eine lokalisierte Ressource vom Typ System.Byte[]. - /// internal static byte[] UpstreamChannelDescriptorTest { get { object obj = ResourceManager.GetObject("UpstreamChannelDescriptorTest", resourceCulture); return ((byte[])(obj)); } } + + internal static byte[] Frame00001343_TSGS1_MIS000_SYNC001 { + get { + object obj = ResourceManager.GetObject("Frame00001343_TSGS1_MIS000_SYNC001", resourceCulture); + return ((byte[])(obj)); + } + } + + internal static byte[] Frame00000008_TSGS1_MIS000_SYNC001 { + get { + object obj = ResourceManager.GetObject("Frame00000008_TSGS1_MIS000_SYNC001", resourceCulture); + return ((byte[])(obj)); + } + } + + internal static byte[] Frame00000012_TSGS1_MIS000_SYNC001 { + get { + object obj = ResourceManager.GetObject("Frame00000012_TSGS1_MIS000_SYNC001", resourceCulture); + return ((byte[])(obj)); + } + } + + internal static byte[] sdpTest { + get { + object obj = ResourceManager.GetObject("sdpTest", resourceCulture); + return ((byte[])(obj)); + } + } + + internal static byte[] Frame00000357_TSGS1_MIS000_SYNC184 { + get { + object obj = ResourceManager.GetObject("Frame00000357_TSGS1_MIS000_SYNC184", resourceCulture); + return ((byte[])(obj)); + } + } } } diff --git a/skyscraper8.Tests/Resources1.resx b/skyscraper8.Tests/Resources1.resx index 906044f..7aca4aa 100644 --- a/skyscraper8.Tests/Resources1.resx +++ b/skyscraper8.Tests/Resources1.resx @@ -160,4 +160,7 @@ Resources\sdpTest.sdp;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - \ No newline at end of file + + Resources\Frame00000357_TSGS1_MIS000_SYNC184.bbframe;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + diff --git a/skyscraper8.sln.DotSettings.user b/skyscraper8.sln.DotSettings.user index 883ca52..b1b6d7e 100644 --- a/skyscraper8.sln.DotSettings.user +++ b/skyscraper8.sln.DotSettings.user @@ -1,6 +1,7 @@  ForceIncluded ForceIncluded + ForceIncluded ForceIncluded ForceIncluded ForceIncluded @@ -39,7 +40,7 @@ <Assembly Path="/home/schiemas/.nuget/packages/allure.net.commons/2.14.1/lib/netstandard2.0/Allure.Net.Commons.dll" /> </AssemblyExplorer> /home/schiemas/.cache/JetBrains/Rider2025.1/resharper-host/temp/Rider/vAny/CoverageData/_skyscraper8.1808907683/Snapshot/snapshot.utdcvr - <SessionState ContinuousTestingMode="0" IsActive="True" Name="All tests from &lt;skyscraper8.Tests&gt;" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> + <SessionState ContinuousTestingMode="0" Name="All tests from &lt;skyscraper8.Tests&gt;" xmlns="urn:schemas-jetbrains-com:jetbrains-ut-session"> <And> <Namespace>skyscraper8.Tests</Namespace> <Project Location="\home\schiemas\RiderProjects\skyscraper8\skyscraper8.Tests" Presentation="&lt;skyscraper8.Tests&gt;" /> @@ -59,4 +60,5 @@ <data><HostParameters type="LocalHostParameters" /><Argument type="StandaloneArgument"><Arguments IsNull="False"></Arguments><FileName IsNull="False"></FileName><WorkingDirectory IsNull="False"></WorkingDirectory><Scope><ProcessFilters /></Scope></Argument><Info type="TimelineInfo" /><CoreOptions type="CoreOptions"><CoreTempPath IsNull="False"></CoreTempPath><RemoteEndPoint IsNull="False"></RemoteEndPoint><AdditionalEnvironmentVariables /></CoreOptions><HostOptions type="HostOptions"><HostTempPath IsNull="False"></HostTempPath></HostOptions></data> True - True + True + False diff --git a/skyscraper8/GS/BBHeader.cs b/skyscraper8/GS/BBHeader.cs index 13228a3..93bd58c 100644 --- a/skyscraper8/GS/BBHeader.cs +++ b/skyscraper8/GS/BBHeader.cs @@ -79,4 +79,9 @@ public class BBHeader : Validatable /// 3 = Transport /// public int TsGs { get; private set; } + + public override string ToString() + { + return String.Format("TsGs: {0}, SyncByte: {1}, UPL: {2}", TsGs, SyncByte,UserPacketLength); + } } \ No newline at end of file diff --git a/skyscraper8/GS/BBframeDeencapsulator3.cs b/skyscraper8/GS/BBframeDeencapsulator3.cs index 094202e..a827251 100644 --- a/skyscraper8/GS/BBframeDeencapsulator3.cs +++ b/skyscraper8/GS/BBframeDeencapsulator3.cs @@ -22,7 +22,7 @@ class BbframeDeencapsulator3 : IBbframeDeencapsulator { if (numPushed == 0) { - logger.InfoFormat("The stream started in the middle of a BBFrame, let's skip to the start of the next one."); + logger.InfoFormat("The stream started in the middle of a BBFrame, let's wait for the next one to start."); } return; } @@ -36,8 +36,7 @@ class BbframeDeencapsulator3 : IBbframeDeencapsulator mis = new IMisHandler[256]; if (mis[bbHeader.Matype2] == null) { - logger.InfoFormat("Found a stream on MIS {0}",bbHeader.Matype2); - mis[bbHeader.Matype2] = new GsTypeDetector(context.MisClone(bbHeader.Matype2)); + mis[bbHeader.Matype2] = new MisHandlerProxy(context.MisClone(bbHeader.Matype2)); } mis[bbHeader.Matype2].PushFrame(bbHeader, new ReadOnlySpan(bbframe, 11, bbframe.Length - 11)); diff --git a/skyscraper8/GS/GSE-RollingSyncByte/GseWithRollingSyncByteReader.cs b/skyscraper8/GS/GSE-RollingSyncByte/GseWithRollingSyncByteReader.cs new file mode 100644 index 0000000..60e11e5 --- /dev/null +++ b/skyscraper8/GS/GSE-RollingSyncByte/GseWithRollingSyncByteReader.cs @@ -0,0 +1,185 @@ +using log4net; +using skyscraper5.Skyscraper.IO; +using skyscraper8.GSE; +using skyscraper8.GSE.GSE; + +namespace skyscraper8.GS.GSE_RollingSyncByte; + +public class GseWithRollingSyncByteReader : IMisHandler +{ + public GsContextDto Context { get; set; } + + private ulong packetSerial; + private GseLabel reuse; + + private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name); + public void PushFrame(BBHeader bbHeader, ReadOnlySpan readOnlySpan) + { + packetSerial++; + byte[] array = readOnlySpan.ToArray(); + MemoryStream ms = new MemoryStream(array, 0, array.Length - 4); + while (ms.GetAvailableBytes() > 0) + { + byte a = ms.ReadUInt8(); + bool startIndicator = (a & 0x80) != 0; + bool endIndicator = (a & 0x40) != 0; + int labelTypeIndicator = (a & 0x30) >> 4; + GsePacket gsePacket = new GsePacket(startIndicator, endIndicator, labelTypeIndicator); + if (!startIndicator && !endIndicator && labelTypeIndicator == 0) + { + return; + } + else + { + byte b = ms.ReadUInt8(); + int gseLength = (a & 0x0f) << 8; + gseLength += b; + int gseRemain = gseLength - 2; + if (!startIndicator || !endIndicator) + { + gsePacket.FragmentId = ms.ReadUInt8(); + gseRemain--; + } + + if (startIndicator && !endIndicator) + { + gsePacket.TotalLength = ms.ReadUInt16BE(); + gseRemain -= 2; + } + + if (startIndicator) + { + gsePacket.ProtocolType = ms.ReadUInt16BE(); + gseRemain -= 2; + switch (labelTypeIndicator) + { + case 0: + if (ms.GetAvailableBytes() < 6) + return; //The MAC address is sliced in half. I have my doubts that this is even allowed. + gsePacket.Label = new _6byteLabel(ms.ReadBytes(6)); + reuse = gsePacket.Label; + gseRemain -= 6; + break; + case 1: + gsePacket.Label = new _3byteLabel(ms.ReadBytes(3)); + reuse = gsePacket.Label; + gseRemain -= 3; + break; + case 2: + gsePacket.Label = BroadcastLabel.GetInstance(); + break; + case 3: + gsePacket.Label = reuse; + break; + } + } + + int payloadSize = gseRemain; + if (!startIndicator && endIndicator) + payloadSize -= 4; + + gsePacket.GseDataBytes = ms.ReadBytes(payloadSize); + gseRemain -= payloadSize; + + if (!startIndicator && endIndicator) + { + gsePacket.Crc32 = ms.ReadUInt32BE(); + gseRemain -= 4; + } + } + + ProcessPacket(gsePacket); + } + } + + private bool[] warnedEthertypes; + private GseFragmentation[] fragments; + + public GseWithRollingSyncByteReader(GsContextDto context) + { + this.Context = context; + } + + private void ProcessPacket(GsePacket gsePacket) + { + if (gsePacket.LabelTypeIndicator == 3 && gsePacket.Label == null) + return; + + if (!gsePacket.StartIndicator && gsePacket.EndIndicator) + { + if (fragments == null) + return; + if (fragments[gsePacket.FragmentId.Value] == null) + return; + + if (fragments[gsePacket.FragmentId.Value].Validate()) + { + byte[] gseDataBytes = fragments[gsePacket.FragmentId.Value].GetGseDataBytes(); + HandleAssembledFrame(fragments[gsePacket.FragmentId.Value].ProtocolType, gseDataBytes, fragments[gsePacket.FragmentId.Value].Label); + } + } + + if (gsePacket.StartIndicator && gsePacket.EndIndicator) + { + Context.InformTrafficType(gsePacket.ProtocolType.Value); + HandleAssembledFrame(gsePacket.ProtocolType.Value, gsePacket.GseDataBytes, gsePacket.Label); + return; + } + + if (gsePacket.StartIndicator && !gsePacket.EndIndicator) + { + if (fragments == null) + fragments = new GseFragmentation[256]; + fragments[gsePacket.FragmentId.Value] = new GseFragmentation(gsePacket); + return; + } + + if (!gsePacket.StartIndicator && !gsePacket.EndIndicator) + { + if (fragments == null) + return; + if (fragments[gsePacket.FragmentId.Value] == null) + return; + fragments[gsePacket.FragmentId.Value].AddFragement(gsePacket); + return; + } + + throw new NotImplementedException(); + } + + private ulong aolSerial; + private void HandleProprietaryAolJunk(byte[] buffer, ushort protocolType) + { + aolSerial++; + string dumpPath = String.Format("{0}{1}{2}.bin", protocolType, Path.DirectorySeparatorChar, aolSerial); + FileInfo dumpFile = new FileInfo(dumpPath); + dumpFile.Directory.EnsureExists(); + File.WriteAllBytes(dumpFile.FullName, buffer); + } + + private void HandleAssembledFrame(ushort protocolType, byte[] buffer, GseLabel label) + { + switch (protocolType) + { + case 0x0002: + case 0x0004: + HandleProprietaryAolJunk(buffer,protocolType); + return; + case 0x0091: + //according to https://www.iana.org/assignments/ule-next-headers/ule-next-headers.xhtml it's private + return; + case 0x0800: + Context.IpOutput.OnIpDatagram(0x010e, buffer); + return; + default: + if (warnedEthertypes == null) + warnedEthertypes = new bool[ushort.MaxValue]; + if (!warnedEthertypes[protocolType]) + { + logger.WarnFormat("This GSE contains other traffic types (type 0x{0:X4}) besides IP (type 0x0800). If it's not too much trouble, please consider submitting a sample.",protocolType); + warnedEthertypes[protocolType] = true; + } + return; + } + } +} diff --git a/skyscraper8/GS/GSE/GseReader.cs b/skyscraper8/GS/GSE/GseReader.cs index 8a7f6c8..089bd07 100644 --- a/skyscraper8/GS/GSE/GseReader.cs +++ b/skyscraper8/GS/GSE/GseReader.cs @@ -7,6 +7,10 @@ namespace skyscraper8.GSE.GSE; internal class GseReader : IMisHandler { + public GseReader(GsContextDto dto) + { + this.Context = dto; + } private GseLabel lastLabel; private static readonly ILog logger = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name); diff --git a/skyscraper8/GS/GsContextDto.cs b/skyscraper8/GS/GsContextDto.cs index dd8434b..feb6d84 100644 --- a/skyscraper8/GS/GsContextDto.cs +++ b/skyscraper8/GS/GsContextDto.cs @@ -10,7 +10,7 @@ using skyscraper8.Skyscraper.Scraper; namespace skyscraper8.GS { - internal class GsContextDto + public class GsContextDto { public GsContextMisDto MisClone(byte mis) { @@ -39,9 +39,21 @@ namespace skyscraper8.GS public IMultiprotocolEncapsulationEventHandler IpOutput { get; set; } public InteractionChannelHandler Rcs2Output { get; set; } + + private Dictionary trafficTypes; + public void InformTrafficType(ushort trafficType) + { + if (trafficTypes == null) + trafficTypes = new Dictionary(); + + if (!trafficTypes.ContainsKey(trafficType)) + trafficTypes.Add(trafficType, 1); + else + trafficTypes[trafficType]++; + } } - internal class GsContextMisDto : GsContextDto + public class GsContextMisDto : GsContextDto { public byte Mis { get; } diff --git a/skyscraper8/GS/GsTypeDetector.cs b/skyscraper8/GS/GsTypeDetector.cs deleted file mode 100644 index a8280a0..0000000 --- a/skyscraper8/GS/GsTypeDetector.cs +++ /dev/null @@ -1,109 +0,0 @@ -using log4net; -using skyscraper5.Dvb.DataBroadcasting; -using skyscraper8.GS; -using skyscraper8.GS.GSE_BFBS; -using skyscraper8.GS.SiminnRadiomidun; -using skyscraper8.GSE.GSE_HEM; -using skyscraper8.GSE.GSE; -using skyscraper8.Skyscraper.Scraper; - -namespace skyscraper8.GSE; - -class GsTypeDetector : IMisHandler -{ - public GsContextDto Context { get; set; } - private readonly ILog logger; - private HashSet> _postedStreamTypes; - - public GsTypeDetector(GsContextMisDto context) - { - this.logger = LogManager.GetLogger(String.Format("{0} MIS {1}",System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name,context.Mis)); - this.Context = context; - } - - private GseReader gseReader; - private GseHemReader gseHemReader; - private SiminnRadiomidunReader siminnRadiomidunReader; - private BfbsGseReader bfbsGseReader; - private int simminRadiomidunScore; - private int bfbsScore; - - public void PushFrame(BBHeader bbHeader, ReadOnlySpan readOnlySpan) - { - if (readOnlySpan.Length == 0) - { - return; - } - - if (bbHeader.TsGs == 1 && bbHeader.SyncByte == 0) - { - //Looks like Continuous GSE. - if (gseReader == null) - { - gseReader = new GseReader(); - gseReader.Context = Context; - } - - gseReader.PushFrame(bbHeader, readOnlySpan); - return; - } - - if (bbHeader.TsGs == 2 && bbHeader.SyncByte == 0) - { - //Looks like GSE-HEM - if (gseHemReader == null) - { - gseHemReader = new GseHemReader(Context); - } - gseHemReader.PushFrame(bbHeader, readOnlySpan); - return; - } - - if (bbHeader.TsGs == 0 && bbHeader.SyncByte == 71 && bbHeader.UserPacketLength == 188) - { - simminRadiomidunScore++; - if (simminRadiomidunScore > 10) - { - //Looks like a Simmin Radiomidun-like stream. - if (siminnRadiomidunReader == null) - { - //These behave similar to T2-MI. Interesting. - siminnRadiomidunReader = new SiminnRadiomidunReader(Context); - } - - siminnRadiomidunReader.PushFrame(bbHeader, readOnlySpan); - return; - } - - return; - } - else - { - if (simminRadiomidunScore < 10) - { - simminRadiomidunScore = 0; - } - } - - if (bbHeader.TsGs == 1 && bbHeader.SyncByte == 1 && bbHeader.UserPacketLength == 0) - { - if (bfbsGseReader == null) - { - bfbsGseReader = new BfbsGseReader(Context); - } - - bfbsGseReader.PushFrame(bbHeader, readOnlySpan); - return; - } - - //We have no idea what this is. - Tuple streamTypeToPost = new Tuple(bbHeader.TsGs, bbHeader.SyncByte); - if (_postedStreamTypes == null) - _postedStreamTypes = new HashSet>(); - if (!_postedStreamTypes.Contains(streamTypeToPost)) - { - logger.WarnFormat("This GS contains packets of type {0} ({2}) with a sync byte of {1}. This is not supported yet. If it isn't too much trouble, please consider sharing a sample of this stream.",streamTypeToPost.Item1,streamTypeToPost.Item2, (TsGsType)streamTypeToPost.Item1); - _postedStreamTypes.Add(streamTypeToPost); - } - } -} \ No newline at end of file diff --git a/skyscraper8/GS/MisHandlerProxy.cs b/skyscraper8/GS/MisHandlerProxy.cs new file mode 100644 index 0000000..c5842bb --- /dev/null +++ b/skyscraper8/GS/MisHandlerProxy.cs @@ -0,0 +1,190 @@ +using log4net; +using skyscraper5.Mpeg2; +using skyscraper8.GS; +using skyscraper8.GS.GSE_BFBS; +using skyscraper8.GS.GSE_RollingSyncByte; +using skyscraper8.GS.SiminnRadiomidun; +using skyscraper8.GSE.GSE_HEM; +using skyscraper8.GSE.GSE; + +namespace skyscraper8.GSE; + +class MisHandlerProxy : IMisHandler +{ + public GsContextDto Context { get; set; } + private readonly ILog logger; + private HashSet> _postedStreamTypes; + + public MisHandlerProxy(GsContextMisDto context) + { + this.logger = LogManager.GetLogger(String.Format("{0} MIS {1}",System.Reflection.MethodBase.GetCurrentMethod().DeclaringType.Name,context.Mis)); + this.Context = context; + } + + private int state; + private Queue> waitingPackets; + private DateTime lastWaitingTimePrinted; + private IMisHandler misHandler; + private bool unknownStateError; + + + public void PushFrame(BBHeader bbHeader, ReadOnlySpan readOnlySpan) + { + if (readOnlySpan.Length == 0) + { + return; + } + + switch (state) + { + case 0: //No packets collected yet. + waitingPackets = new Queue>(); + logger.InfoFormat("Attempting to detect the stream type of MIS {0}...",bbHeader.Matype2); + waitingPackets.Enqueue(new Tuple(bbHeader, readOnlySpan.ToArray())); + lastWaitingTimePrinted = DateTime.Now; + state = 1; + break; + case 1: + DateTime currentTime = DateTime.Now; + if (currentTime - lastWaitingTimePrinted > TimeSpan.FromSeconds(1)) + { + logger.InfoFormat("Waiting for {0} more packets on MIS {1}...",100 - waitingPackets.Count,bbHeader.Matype2); + lastWaitingTimePrinted = currentTime; + } + waitingPackets.Enqueue(new Tuple(bbHeader, readOnlySpan.ToArray())); + if (waitingPackets.Count == 100) + { + state = 2; + } + break; + case 2: + RunDetection(); + DrainQueue(); + misHandler.PushFrame(bbHeader, readOnlySpan); + state = 3; + break; + case 3: + misHandler.PushFrame(bbHeader, readOnlySpan); + break; + default: + if (!unknownStateError) + { + logger.FatalFormat("The Finite-state machine of the MIS Handler reached an impossible state. This is a bug in skyscraper8. It would be great if you could share a sample of this stream to reproduce this, so I can fix this."); + unknownStateError = true; + } + break; + } + } + + private enum DetectedGsType + { + ContinuousGSE, + GSE_HEM, + SimminRadiomidun, + BfbsGSE, + GseWithRollingSyncByte + } + + private void SucessfulDetection(DetectedGsType detectedGsType) + { + logger.InfoFormat("MIS {0} looks like {1}, will try to treat it as such.",Context.GetMisId(),detectedGsType); + switch (detectedGsType) + { + case DetectedGsType.ContinuousGSE: misHandler = new GseReader(Context); break; + case DetectedGsType.GSE_HEM: misHandler = new GseHemReader(Context); break; + case DetectedGsType.SimminRadiomidun: misHandler = new SiminnRadiomidunReader(Context); break; + case DetectedGsType.BfbsGSE: misHandler = new BfbsGseReader(Context); break; + case DetectedGsType.GseWithRollingSyncByte: misHandler = new GseWithRollingSyncByteReader(Context); break; + default: + logger.FatalFormat("However, {0} is not implemented yet, sorry...", detectedGsType); + misHandler = new NullMisHandler(); + break; + } + } + + private bool RunDetection() + { + //bbHeader.TsGs == 1 && bbHeader.SyncByte == 0 + int continuousGseScore = waitingPackets.Count(x => x.Item1.TsGs == 1 && x.Item1.SyncByte == 0); + if (continuousGseScore == 100) + { + SucessfulDetection(DetectedGsType.ContinuousGSE); + return true; + } + + //bbHeader.TsGs == 2 && bbHeader.SyncByte == 0 + int gseHemScore = waitingPackets.Count(x => x.Item1.TsGs == 2 && x.Item1.SyncByte == 0); + if (gseHemScore == 100) + { + SucessfulDetection(DetectedGsType.GSE_HEM); + return true; + } + + //bbHeader.TsGs == 0 && bbHeader.SyncByte == 71 && bbHeader.UserPacketLength == 188 + int simminRadiomidunScore = waitingPackets.Count(x => x.Item1.TsGs == 0 && x.Item1.SyncByte == 71 && x.Item1.UserPacketLength == 188); + if (simminRadiomidunScore == 100) + { + SucessfulDetection(DetectedGsType.SimminRadiomidun); + return true; + } + + //bbHeader.TsGs == 1 && bbHeader.SyncByte == 1 && bbHeader.UserPacketLength == 0 + int bfbsGseScore = waitingPackets.Count(x => x.Item1.TsGs == 1 && x.Item1.SyncByte == 1 && x.Item1.UserPacketLength == 0); + if (bfbsGseScore == 100) + { + SucessfulDetection(DetectedGsType.BfbsGSE); + return true; + } + + bool allPacketsHaveValidCrc32 = AnalyzeCrc32Values(); + + if (!allPacketsHaveValidCrc32) + { + if (StatisticalAnalysis_Ses12_11476v()) + { + SucessfulDetection(DetectedGsType.GseWithRollingSyncByte); + return true; + } + } + + return false; + } + + private void DrainQueue() + { + while (waitingPackets.Count > 0) + { + Tuple dequeue = waitingPackets.Dequeue(); + misHandler.PushFrame(dequeue.Item1, dequeue.Item2); + } + waitingPackets = null; + } + + private bool AnalyzeCrc32Values() + { + foreach (Tuple waitingPacket in waitingPackets) + { + bool validateCrc = DvbCrc32.ValidateCrc(waitingPacket.Item2); + if (!validateCrc) + return false; + } + + return true; + } + + private bool StatisticalAnalysis_Ses12_11476v() + { + byte[] syncBytes = new byte[256]; + foreach (Tuple dequeue in waitingPackets) + { + syncBytes[dequeue.Item1.SyncByte]++; + if (syncBytes[dequeue.Item1.SyncByte] > 3) + return false; + + if (dequeue.Item1.DataFieldLength != dequeue.Item2.Length) + return false; + } + + return true; + } +} \ No newline at end of file diff --git a/skyscraper8/GS/NullMisHandler.cs b/skyscraper8/GS/NullMisHandler.cs new file mode 100644 index 0000000..42a0bed --- /dev/null +++ b/skyscraper8/GS/NullMisHandler.cs @@ -0,0 +1,12 @@ +using skyscraper8.GSE; + +namespace skyscraper8.GS; + +class NullMisHandler : IMisHandler +{ + public GsContextDto Context { get; set; } + public void PushFrame(BBHeader bbHeader, ReadOnlySpan readOnlySpan) + { + + } +} diff --git a/skyscraper8/Id3/Id3ErrorState.cs b/skyscraper8/Id3/Id3ErrorState.cs index 9d30317..08f7590 100644 --- a/skyscraper8/Id3/Id3ErrorState.cs +++ b/skyscraper8/Id3/Id3ErrorState.cs @@ -9,6 +9,7 @@ namespace skyscraper5.src.Id3 internal enum Id3ErrorState { InvalidMagic, - PacketTooShort + PacketTooShort, + NullPacket } } diff --git a/skyscraper8/Id3/Id3PesProcessor.cs b/skyscraper8/Id3/Id3PesProcessor.cs index ef2aeaa..f553a39 100644 --- a/skyscraper8/Id3/Id3PesProcessor.cs +++ b/skyscraper8/Id3/Id3PesProcessor.cs @@ -22,6 +22,12 @@ namespace skyscraper5.src.Id3 public void ProcessPesPacket(PesPacket packet) { + if (packet.Payload == null) + { + handler.OnId3Error(Id3ErrorState.NullPacket); + return; + } + MemoryStream ms = new MemoryStream(packet.Payload, false); if (ms.ReadUInt8() != 'I') { diff --git a/skyscraper8/InteractionChannel/InteractionChannelErrorState.cs b/skyscraper8/InteractionChannel/InteractionChannelErrorState.cs index 8b05d63..83b2606 100644 --- a/skyscraper8/InteractionChannel/InteractionChannelErrorState.cs +++ b/skyscraper8/InteractionChannel/InteractionChannelErrorState.cs @@ -6,7 +6,7 @@ using System.Threading.Tasks; namespace skyscraper5.src.InteractionChannel { - internal enum InteractionChannelErrorState + public enum InteractionChannelErrorState { UnexpectedTable, HeaderInvalid, diff --git a/skyscraper8/InteractionChannel/InteractionChannelHandler.cs b/skyscraper8/InteractionChannel/InteractionChannelHandler.cs index 7fa0d7d..c5424f1 100644 --- a/skyscraper8/InteractionChannel/InteractionChannelHandler.cs +++ b/skyscraper8/InteractionChannel/InteractionChannelHandler.cs @@ -15,7 +15,7 @@ using skyscraper8.InteractionChannel.Model2.Descriptors; namespace skyscraper5.src.InteractionChannel { - internal interface InteractionChannelHandler + public interface InteractionChannelHandler { void OnCorrectionMessage(ushort interactiveNetworkId, Cmt cmt); void OnFrameComposition(ushort interactiveNetworkId, Fct fct); diff --git a/skyscraper8/InteractionChannel/Model/Tmst.cs b/skyscraper8/InteractionChannel/Model/Tmst.cs index 7894452..3f149ce 100644 --- a/skyscraper8/InteractionChannel/Model/Tmst.cs +++ b/skyscraper8/InteractionChannel/Model/Tmst.cs @@ -9,7 +9,7 @@ using System.Threading.Tasks; namespace skyscraper5.src.InteractionChannel.Model { - internal class Tmst : Validatable + public class Tmst : Validatable { public Tmst(MemoryStream ms) { diff --git a/skyscraper8/Mpeg2/Crc32.cs b/skyscraper8/Mpeg2/Crc32.cs index b2fb901..e109925 100644 --- a/skyscraper8/Mpeg2/Crc32.cs +++ b/skyscraper8/Mpeg2/Crc32.cs @@ -125,6 +125,20 @@ namespace skyscraper5.Mpeg2 return crc == 0; } + + public static uint CreateCrc(ReadOnlySpan data) + { + uint crc = 0xffffffff; + + for (int i = 0; i < data.Length; i++) + { + uint b = (crc >> 24) & 0xff; + int c = (data[i]) & 0xff; + crc = (crc << 8) ^ table[b ^ c]; + } + + return crc; + } public static uint CreateCrc(MemoryStream ms, int offset, int end) { diff --git a/skyscraper8/Program.cs b/skyscraper8/Program.cs index 810207b..85c94c7 100644 --- a/skyscraper8/Program.cs +++ b/skyscraper8/Program.cs @@ -39,8 +39,9 @@ namespace skyscraper5 private static void IntegrationTest() { - SoftcamTestProgram softcamTestProgram = new SoftcamTestProgram(); - softcamTestProgram.Run(); + //SoftcamTestProgram softcamTestProgram = new SoftcamTestProgram(); + //softcamTestProgram.Run(); + /*List ssdpDevices = SsdpClient.GetSsdpDevices(1000).ToList(); foreach (SsdpDevice ssdpDevice in ssdpDevices) {