Sfoglia il codice sorgente

Merge pull request #5520 from Bond-009/hdhomerun2

Bond-009 4 anni fa
parent
commit
631df8c560

+ 40 - 37
Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunManager.cs

@@ -130,9 +130,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
 
                 int receivedBytes = await stream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);
 
-                ParseReturnMessage(buffer, receivedBytes, out string returnVal);
-
-                return string.Equals(returnVal, "none", StringComparison.OrdinalIgnoreCase);
+                return VerifyReturnValueOfGetSet(buffer.AsSpan(receivedBytes), "none");
             }
             finally
             {
@@ -173,7 +171,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                     int receivedBytes = await stream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);
 
                     // parse response to make sure it worked
-                    if (!ParseReturnMessage(buffer, receivedBytes, out _))
+                    if (!TryGetReturnValueOfGetSet(buffer.AsSpan(0, receivedBytes), out _))
                     {
                         continue;
                     }
@@ -185,7 +183,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                         receivedBytes = await stream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);
 
                         // parse response to make sure it worked
-                        if (!ParseReturnMessage(buffer, receivedBytes, out _))
+                        if (!TryGetReturnValueOfGetSet(buffer.AsSpan(0, receivedBytes), out _))
                         {
                             await ReleaseLockkey(_tcpClient, lockKeyValue).ConfigureAwait(false);
                             continue;
@@ -199,7 +197,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                     receivedBytes = await stream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);
 
                     // parse response to make sure it worked
-                    if (!ParseReturnMessage(buffer, receivedBytes, out _))
+                    if (!TryGetReturnValueOfGetSet(buffer.AsSpan(0, receivedBytes), out _))
                     {
                         await ReleaseLockkey(_tcpClient, lockKeyValue).ConfigureAwait(false);
                         continue;
@@ -239,7 +237,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
                     int receivedBytes = await stream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false);
 
                     // parse response to make sure it worked
-                    if (!ParseReturnMessage(buffer, receivedBytes, out _))
+                    if (!TryGetReturnValueOfGetSet(buffer.AsSpan(0, receivedBytes), out _))
                     {
                         return;
                     }
@@ -292,7 +290,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
             return FinishPacket(buffer, offset);
         }
 
-        private static int WriteSetMessage(Span<byte> buffer, int tuner, string name, string value, uint? lockkey)
+        internal static int WriteSetMessage(Span<byte> buffer, int tuner, string name, string value, uint? lockkey)
         {
             var byteName = string.Format(CultureInfo.InvariantCulture, "/tuner{0}/{1}", tuner, name);
             int offset = WriteHeaderAndPayload(buffer, byteName);
@@ -355,60 +353,65 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
             return offset + 4;
         }
 
-        private static bool ParseReturnMessage(byte[] buf, int numBytes, out string returnVal)
+        internal static bool VerifyReturnValueOfGetSet(ReadOnlySpan<byte> buffer, string expected)
         {
-            returnVal = string.Empty;
+            return TryGetReturnValueOfGetSet(buffer, out var value)
+                && string.Equals(Encoding.UTF8.GetString(value), expected, StringComparison.OrdinalIgnoreCase);
+        }
 
-            if (numBytes < 4)
+        internal static bool TryGetReturnValueOfGetSet(ReadOnlySpan<byte> buffer, out ReadOnlySpan<byte> value)
+        {
+            value = ReadOnlySpan<byte>.Empty;
+
+            if (buffer.Length < 8)
             {
                 return false;
             }
 
-            var flipEndian = BitConverter.IsLittleEndian;
-            int offset = 0;
-            byte[] msgTypeBytes = new byte[2];
-            Buffer.BlockCopy(buf, offset, msgTypeBytes, 0, msgTypeBytes.Length);
-
-            if (flipEndian)
+            uint crc = BinaryPrimitives.ReadUInt32LittleEndian(buffer[^4..]);
+            if (crc != Crc32.Compute(buffer[..^4]))
             {
-                Array.Reverse(msgTypeBytes);
+                return false;
             }
 
-            var msgType = BitConverter.ToUInt16(msgTypeBytes, 0);
-            offset += 2;
-
-            if (msgType != GetSetReply)
+            if (BinaryPrimitives.ReadUInt16BigEndian(buffer) != GetSetReply)
             {
                 return false;
             }
 
-            byte[] msgLengthBytes = new byte[2];
-            Buffer.BlockCopy(buf, offset, msgLengthBytes, 0, msgLengthBytes.Length);
-            if (flipEndian)
+            var msgLength = BinaryPrimitives.ReadUInt16BigEndian(buffer.Slice(2));
+            if (buffer.Length != 2 + 2 + 4 + msgLength)
             {
-                Array.Reverse(msgLengthBytes);
+                return false;
             }
 
-            var msgLength = BitConverter.ToUInt16(msgLengthBytes, 0);
-            offset += 2;
-
-            if (numBytes < msgLength + 8)
+            var offset = 4;
+            if (buffer[offset++] != GetSetName)
             {
                 return false;
             }
 
-            offset++; // Name Tag
-
-            var nameLength = buf[offset++];
+            var nameLength = buffer[offset++];
+            if (buffer.Length < 4 + 1 + offset + nameLength)
+            {
+                return false;
+            }
 
-            // skip the name field to get to value for return
             offset += nameLength;
 
-            offset++; // Value Tag
+            if (buffer[offset++] != GetSetValue)
+            {
+                return false;
+            }
 
-            var valueLength = buf[offset++];
+            var valueLength = buffer[offset++];
+            if (buffer.Length < 4 + offset + valueLength)
+            {
+                return false;
+            }
 
-            returnVal = Encoding.UTF8.GetString(buf, offset, valueLength - 1); // remove null terminator
+            // remove null terminator
+            value = buffer.Slice(offset, valueLength - 1);
             return true;
         }
     }

+ 274 - 6
tests/Jellyfin.Server.Implementations.Tests/LiveTv/HdHomerunManagerTests.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Text;
 using Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun;
 using Xunit;
 
@@ -17,8 +18,9 @@ namespace Jellyfin.Server.Implementations.Tests.LiveTv
             Span<byte> buffer = stackalloc byte[128];
             int len = HdHomerunManager.WriteNullTerminatedString(buffer, string.Empty);
 
-            Assert.Equal(expected.Length, len);
-            Assert.True(expected.SequenceEqual(buffer.Slice(0, len)));
+            Assert.Equal(
+                Convert.ToHexString(expected),
+                Convert.ToHexString(buffer.Slice(0, len)));
         }
 
         [Fact]
@@ -32,8 +34,9 @@ namespace Jellyfin.Server.Implementations.Tests.LiveTv
             Span<byte> buffer = stackalloc byte[128];
             int len = HdHomerunManager.WriteNullTerminatedString(buffer, "The quick");
 
-            Assert.Equal(expected.Length, len);
-            Assert.True(expected.SequenceEqual(buffer.Slice(0, len)));
+            Assert.Equal(
+                Convert.ToHexString(expected),
+                Convert.ToHexString(buffer.Slice(0, len)));
         }
 
         [Fact]
@@ -51,8 +54,273 @@ namespace Jellyfin.Server.Implementations.Tests.LiveTv
             Span<byte> buffer = stackalloc byte[128];
             int len = HdHomerunManager.WriteGetMessage(buffer, 0, "N");
 
-            Assert.Equal(expected.Length, len);
-            Assert.True(expected.SequenceEqual(buffer.Slice(0, len)));
+            Assert.Equal(
+                Convert.ToHexString(expected),
+                Convert.ToHexString(buffer.Slice(0, len)));
+        }
+
+        [Fact]
+        public void WriteSetMessage_NoLockKey_Success()
+        {
+            ReadOnlySpan<byte> expected = stackalloc byte[]
+            {
+                0, 4,
+                0, 20,
+                3,
+                10, (byte)'/', (byte)'t', (byte)'u', (byte)'n', (byte)'e', (byte)'r', (byte)'0', (byte)'/', (byte)'N', 0,
+                4,
+                6, (byte)'v', (byte)'a', (byte)'l', (byte)'u', (byte)'e', 0,
+                0xa9, 0x49, 0xd0, 0x68
+            };
+
+            Span<byte> buffer = stackalloc byte[128];
+            int len = HdHomerunManager.WriteSetMessage(buffer, 0, "N", "value", null);
+
+            Assert.Equal(
+                Convert.ToHexString(expected),
+                Convert.ToHexString(buffer.Slice(0, len)));
+        }
+
+        [Fact]
+        public void WriteSetMessage_LockKey_Success()
+        {
+            ReadOnlySpan<byte> expected = stackalloc byte[]
+            {
+                0, 4,
+                0, 26,
+                3,
+                10, (byte)'/', (byte)'t', (byte)'u', (byte)'n', (byte)'e', (byte)'r', (byte)'0', (byte)'/', (byte)'N', 0,
+                4,
+                6, (byte)'v', (byte)'a', (byte)'l', (byte)'u', (byte)'e', 0,
+                21,
+                4, 0x00, 0x01, 0x38, 0xd5,
+                0x8e, 0xb6, 0x06, 0x82
+            };
+
+            Span<byte> buffer = stackalloc byte[128];
+            int len = HdHomerunManager.WriteSetMessage(buffer, 0, "N", "value", 80085);
+
+            Assert.Equal(
+                Convert.ToHexString(expected),
+                Convert.ToHexString(buffer.Slice(0, len)));
+        }
+
+        [Fact]
+        public void TryGetReturnValueOfGetSet_Valid_Success()
+        {
+            ReadOnlySpan<byte> packet = new byte[]
+            {
+                0, 5,
+                0, 20,
+                3,
+                10, (byte)'/', (byte)'t', (byte)'u', (byte)'n', (byte)'e', (byte)'r', (byte)'0', (byte)'/', (byte)'N', 0,
+                4,
+                6, (byte)'v', (byte)'a', (byte)'l', (byte)'u', (byte)'e', 0,
+                0x7d, 0xa3, 0xa3, 0xf3
+            };
+
+            Assert.True(HdHomerunManager.TryGetReturnValueOfGetSet(packet, out var value));
+            Assert.Equal("value", Encoding.UTF8.GetString(value));
+        }
+
+        [Fact]
+        public void TryGetReturnValueOfGetSet_InvalidCrc_False()
+        {
+            ReadOnlySpan<byte> packet = new byte[]
+            {
+                0, 5,
+                0, 20,
+                3,
+                10, (byte)'/', (byte)'t', (byte)'u', (byte)'n', (byte)'e', (byte)'r', (byte)'0', (byte)'/', (byte)'N', 0,
+                4,
+                6, (byte)'v', (byte)'a', (byte)'l', (byte)'u', (byte)'e', 0,
+                0x7d, 0xa3, 0xa3, 0xf4
+            };
+
+            Assert.False(HdHomerunManager.TryGetReturnValueOfGetSet(packet, out _));
+        }
+
+        [Fact]
+        public void TryGetReturnValueOfGetSet_InvalidPacketType_False()
+        {
+            ReadOnlySpan<byte> packet = new byte[]
+            {
+                0, 4,
+                0, 20,
+                3,
+                10, (byte)'/', (byte)'t', (byte)'u', (byte)'n', (byte)'e', (byte)'r', (byte)'0', (byte)'/', (byte)'N', 0,
+                4,
+                6, (byte)'v', (byte)'a', (byte)'l', (byte)'u', (byte)'e', 0,
+                0xa9, 0x49, 0xd0, 0x68
+            };
+
+            Assert.False(HdHomerunManager.TryGetReturnValueOfGetSet(packet, out _));
+        }
+
+        [Fact]
+        public void TryGetReturnValueOfGetSet_InvalidPacket_False()
+        {
+            ReadOnlySpan<byte> packet = new byte[]
+            {
+                0, 5,
+                0, 20,
+                0x7d, 0xa3, 0xa3
+            };
+
+            Assert.False(HdHomerunManager.TryGetReturnValueOfGetSet(packet, out _));
+        }
+
+        [Fact]
+        public void TryGetReturnValueOfGetSet_TooSmallMessageLength_False()
+        {
+            ReadOnlySpan<byte> packet = new byte[]
+            {
+                0, 5,
+                0, 19,
+                3,
+                10, (byte)'/', (byte)'t', (byte)'u', (byte)'n', (byte)'e', (byte)'r', (byte)'0', (byte)'/', (byte)'N', 0,
+                4,
+                6, (byte)'v', (byte)'a', (byte)'l', (byte)'u', (byte)'e', 0,
+                0x25, 0x25, 0x44, 0x9a
+            };
+
+            Assert.False(HdHomerunManager.TryGetReturnValueOfGetSet(packet, out _));
+        }
+
+        [Fact]
+        public void TryGetReturnValueOfGetSet_TooLargeMessageLength_False()
+        {
+            ReadOnlySpan<byte> packet = new byte[]
+            {
+                0, 5,
+                0, 21,
+                3,
+                10, (byte)'/', (byte)'t', (byte)'u', (byte)'n', (byte)'e', (byte)'r', (byte)'0', (byte)'/', (byte)'N', 0,
+                4,
+                6, (byte)'v', (byte)'a', (byte)'l', (byte)'u', (byte)'e', 0,
+                0xe3, 0x20, 0x79, 0x6c
+            };
+
+            Assert.False(HdHomerunManager.TryGetReturnValueOfGetSet(packet, out _));
+        }
+
+        [Fact]
+        public void TryGetReturnValueOfGetSet_TooLargeNameLength_False()
+        {
+            ReadOnlySpan<byte> packet = new byte[]
+            {
+                0, 5,
+                0, 20,
+                3,
+                20, (byte)'/', (byte)'t', (byte)'u', (byte)'n', (byte)'e', (byte)'r', (byte)'0', (byte)'/', (byte)'N', 0,
+                4,
+                6, (byte)'v', (byte)'a', (byte)'l', (byte)'u', (byte)'e', 0,
+                0xe1, 0x8e, 0x9c, 0x74
+            };
+
+            Assert.False(HdHomerunManager.TryGetReturnValueOfGetSet(packet, out _));
+        }
+
+        [Fact]
+        public void TryGetReturnValueOfGetSet_InvalidGetSetNameTag_False()
+        {
+            ReadOnlySpan<byte> packet = new byte[]
+            {
+                0, 5,
+                0, 20,
+                4,
+                10, (byte)'/', (byte)'t', (byte)'u', (byte)'n', (byte)'e', (byte)'r', (byte)'0', (byte)'/', (byte)'N', 0,
+                4,
+                6, (byte)'v', (byte)'a', (byte)'l', (byte)'u', (byte)'e', 0,
+                0xee, 0x05, 0xe7, 0x12
+            };
+
+            Assert.False(HdHomerunManager.TryGetReturnValueOfGetSet(packet, out _));
+        }
+
+        [Fact]
+        public void TryGetReturnValueOfGetSet_InvalidGetSetValueTag_False()
+        {
+            ReadOnlySpan<byte> packet = new byte[]
+            {
+                0, 5,
+                0, 20,
+                3,
+                10, (byte)'/', (byte)'t', (byte)'u', (byte)'n', (byte)'e', (byte)'r', (byte)'0', (byte)'/', (byte)'N', 0,
+                3,
+                6, (byte)'v', (byte)'a', (byte)'l', (byte)'u', (byte)'e', 0,
+                0x64, 0xaa, 0x66, 0xf9
+            };
+
+            Assert.False(HdHomerunManager.TryGetReturnValueOfGetSet(packet, out _));
+        }
+
+        [Fact]
+        public void TryGetReturnValueOfGetSet_TooLargeValueLength_False()
+        {
+            ReadOnlySpan<byte> packet = new byte[]
+            {
+                0, 5,
+                0, 20,
+                3,
+                10, (byte)'/', (byte)'t', (byte)'u', (byte)'n', (byte)'e', (byte)'r', (byte)'0', (byte)'/', (byte)'N', 0,
+                4,
+                7, (byte)'v', (byte)'a', (byte)'l', (byte)'u', (byte)'e', 0,
+                0xc9, 0xa8, 0xd4, 0x55
+            };
+
+            Assert.False(HdHomerunManager.TryGetReturnValueOfGetSet(packet, out _));
+        }
+
+        [Fact]
+        public void VerifyReturnValueOfGetSet_Valid_True()
+        {
+            ReadOnlySpan<byte> packet = new byte[]
+            {
+                0, 5,
+                0, 20,
+                3,
+                10, (byte)'/', (byte)'t', (byte)'u', (byte)'n', (byte)'e', (byte)'r', (byte)'0', (byte)'/', (byte)'N', 0,
+                4,
+                6, (byte)'v', (byte)'a', (byte)'l', (byte)'u', (byte)'e', 0,
+                0x7d, 0xa3, 0xa3, 0xf3
+            };
+
+            Assert.True(HdHomerunManager.VerifyReturnValueOfGetSet(packet, "value"));
+        }
+
+        [Fact]
+        public void VerifyReturnValueOfGetSet_WrongValue_False()
+        {
+            ReadOnlySpan<byte> packet = new byte[]
+            {
+                0, 5,
+                0, 20,
+                3,
+                10, (byte)'/', (byte)'t', (byte)'u', (byte)'n', (byte)'e', (byte)'r', (byte)'0', (byte)'/', (byte)'N', 0,
+                4,
+                6, (byte)'v', (byte)'a', (byte)'l', (byte)'u', (byte)'e', 0,
+                0x7d, 0xa3, 0xa3, 0xf3
+            };
+
+            Assert.False(HdHomerunManager.VerifyReturnValueOfGetSet(packet, "none"));
+        }
+
+        [Fact]
+        public void VerifyReturnValueOfGetSet_InvalidPacket_False()
+        {
+            ReadOnlySpan<byte> packet = new byte[]
+            {
+                0, 4,
+                0, 20,
+                3,
+                10, (byte)'/', (byte)'t', (byte)'u', (byte)'n', (byte)'e', (byte)'r', (byte)'0', (byte)'/', (byte)'N', 0,
+                4,
+                6, (byte)'v', (byte)'a', (byte)'l', (byte)'u', (byte)'e', 0,
+                0x7d, 0xa3, 0xa3, 0xf3
+            };
+
+            Assert.False(HdHomerunManager.VerifyReturnValueOfGetSet(packet, "value"));
         }
     }
 }