2
0
Эх сурвалжийг харах

Rewrite HdHomerunManager.ParseReturnMessage

Bond_009 4 жил өмнө
parent
commit
f9640f4366

+ 39 - 36
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;
                     }
@@ -355,60 +353,65 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
             return offset + 4;
         }
 
-        internal 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;
         }
     }

+ 205 - 4
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;
 
@@ -105,9 +106,9 @@ namespace Jellyfin.Server.Implementations.Tests.LiveTv
         }
 
         [Fact]
-        public void ParseReturnMessage_Valid_Success()
+        public void TryGetReturnValueOfGetSet_Valid_Success()
         {
-            ReadOnlySpan<byte> packet = stackalloc byte[]
+            ReadOnlySpan<byte> packet = new byte[]
             {
                 0, 5,
                 0, 20,
@@ -118,8 +119,208 @@ namespace Jellyfin.Server.Implementations.Tests.LiveTv
                 0x7d, 0xa3, 0xa3, 0xf3
             };
 
-            Assert.True(HdHomerunManager.ParseReturnMessage(packet.ToArray(), packet.Length, out var value));
-            Assert.Equal("value", value);
+            Assert.True(HdHomerunManager.TryGetReturnValueOfGetSet(packet, out var value));
+            Assert.Equal("value", Encoding.UTF8.GetString(value));
+        }
+
+        [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,
+                0x7d, 0xa3, 0xa3, 0xf3
+            };
+
+            Assert.False(HdHomerunManager.TryGetReturnValueOfGetSet(packet, out _));
+        }
+
+        [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_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"));
         }
     }
 }