Merge pull request #5520 from Bond-009/hdhomerun2

This commit is contained in:
Bond-009 2021-04-08 00:27:58 +02:00 committed by GitHub
commit 631df8c560
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 319 additions and 48 deletions

View File

@ -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)
{
Array.Reverse(msgTypeBytes);
}
var msgType = BitConverter.ToUInt16(msgTypeBytes, 0);
offset += 2;
if (msgType != GetSetReply)
uint crc = BinaryPrimitives.ReadUInt32LittleEndian(buffer[^4..]);
if (crc != Crc32.Compute(buffer[..^4]))
{
return false;
}
byte[] msgLengthBytes = new byte[2];
Buffer.BlockCopy(buf, offset, msgLengthBytes, 0, msgLengthBytes.Length);
if (flipEndian)
{
Array.Reverse(msgLengthBytes);
}
var msgLength = BitConverter.ToUInt16(msgLengthBytes, 0);
offset += 2;
if (numBytes < msgLength + 8)
if (BinaryPrimitives.ReadUInt16BigEndian(buffer) != GetSetReply)
{
return false;
}
offset++; // Name Tag
var msgLength = BinaryPrimitives.ReadUInt16BigEndian(buffer.Slice(2));
if (buffer.Length != 2 + 2 + 4 + msgLength)
{
return false;
}
var nameLength = buf[offset++];
var offset = 4;
if (buffer[offset++] != GetSetName)
{
return false;
}
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;
}
}

View File

@ -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"));
}
}
}