Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Tmds.DBus.Protocol/DBusConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ private async Task<InnerConnection> DoConnectAsync()
ThrowHelper.ThrowIfDisposed(_disposed, this);

// Throw DisconnectedException or ConnectException.
if (exception is DisconnectedException || exception is ConnectException)
if (exception is DBusConnectionException)
{
throw;
}
Expand Down
23 changes: 23 additions & 0 deletions src/Tmds.DBus.Protocol/DBusReaderException.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
namespace Tmds.DBus.Protocol;

/// <summary>
/// Exception thrown when the <see cref="Reader"/> encounters unexpected input.
/// This indicates the message is malformed or the read operation doesn't match the actual message format.
/// </summary>
public class DBusReaderException : Exception
{
/// <summary>
/// Initializes a new instance of the DBusReaderException class.
/// </summary>
/// <param name="message">The error message.</param>
public DBusReaderException(string message) : base(message)
{ }

/// <summary>
/// Initializes a new instance of the DBusReaderException class.
/// </summary>
/// <param name="message">The error message.</param>
/// <param name="innerException">The inner exception that caused this exception.</param>
public DBusReaderException(string message, Exception innerException) : base(message, innerException)
{ }
}
3 changes: 2 additions & 1 deletion src/Tmds.DBus.Protocol/Message.cs
Original file line number Diff line number Diff line change
Expand Up @@ -329,7 +329,8 @@ private void ParseHeader(UnixFdCollection? handles, bool isMonitor)
{
if (handles is null || UnixFdCount > handles.Count)
{
throw new ProtocolException("Received less handles than UNIX_FDS.");
// Throw DBusReaderException just as we would when trying to read one of these handles which is out of range.
throw new DBusReaderException("Received less handles than UNIX_FDS.");
}
if (_handles is null)
{
Expand Down
4 changes: 2 additions & 2 deletions src/Tmds.DBus.Protocol/MessageStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@
{
if (guid != authenticationResult.Guid)
{
throw new ConnectException("Authentication failure: Unexpected GUID");

Check warning on line 212 in src/Tmds.DBus.Protocol/MessageStream.cs

View workflow job for this annotation

GitHub Actions / build

'ConnectException' is obsolete: 'Use DBusConnectFailedException instead.'

Check warning on line 212 in src/Tmds.DBus.Protocol/MessageStream.cs

View workflow job for this annotation

GitHub Actions / build

'ConnectException' is obsolete: 'Use DBusConnectFailedException instead.'

Check warning on line 212 in src/Tmds.DBus.Protocol/MessageStream.cs

View workflow job for this annotation

GitHub Actions / build

'ConnectException' is obsolete: 'Use DBusConnectFailedException instead.'

Check warning on line 212 in src/Tmds.DBus.Protocol/MessageStream.cs

View workflow job for this annotation

GitHub Actions / build

'ConnectException' is obsolete: 'Use DBusConnectFailedException instead.'

Check warning on line 212 in src/Tmds.DBus.Protocol/MessageStream.cs

View workflow job for this annotation

GitHub Actions / build

'ConnectException' is obsolete: 'Use DBusConnectFailedException instead.'
}
}

Expand Down Expand Up @@ -237,7 +237,7 @@
return result;
}

throw new ConnectException("Authentication failure");

Check warning on line 240 in src/Tmds.DBus.Protocol/MessageStream.cs

View workflow job for this annotation

GitHub Actions / build

'ConnectException' is obsolete: 'Use DBusConnectFailedException instead.'

Check warning on line 240 in src/Tmds.DBus.Protocol/MessageStream.cs

View workflow job for this annotation

GitHub Actions / build

'ConnectException' is obsolete: 'Use DBusConnectFailedException instead.'

Check warning on line 240 in src/Tmds.DBus.Protocol/MessageStream.cs

View workflow job for this annotation

GitHub Actions / build

'ConnectException' is obsolete: 'Use DBusConnectFailedException instead.'

Check warning on line 240 in src/Tmds.DBus.Protocol/MessageStream.cs

View workflow job for this annotation

GitHub Actions / build

'ConnectException' is obsolete: 'Use DBusConnectFailedException instead.'

Check warning on line 240 in src/Tmds.DBus.Protocol/MessageStream.cs

View workflow job for this annotation

GitHub Actions / build

'ConnectException' is obsolete: 'Use DBusConnectFailedException instead.'
}

private static string CreateAuthExternalCommand(string userId)
Expand Down Expand Up @@ -399,11 +399,11 @@
span = span.Slice(0, (int)src.Length);
if (!span.EndsWith((ReadOnlySpan<byte>)new byte[] { (byte)'\r' }))
{
throw new ProtocolException("Authentication messages from server must end with '\\r\\n'.");
throw new ConnectException("Authentication messages from server must end with '\\r\\n'.");

Check warning on line 402 in src/Tmds.DBus.Protocol/MessageStream.cs

View workflow job for this annotation

GitHub Actions / build

'ConnectException' is obsolete: 'Use DBusConnectFailedException instead.'

Check warning on line 402 in src/Tmds.DBus.Protocol/MessageStream.cs

View workflow job for this annotation

GitHub Actions / build

'ConnectException' is obsolete: 'Use DBusConnectFailedException instead.'
}
if (span.Length == 1)
{
throw new ProtocolException("Received empty authentication message from server.");
throw new ConnectException("Received empty authentication message from server.");

Check warning on line 406 in src/Tmds.DBus.Protocol/MessageStream.cs

View workflow job for this annotation

GitHub Actions / build

'ConnectException' is obsolete: 'Use DBusConnectFailedException instead.'
}
return span.Length - 1;
}
Expand Down
2 changes: 1 addition & 1 deletion src/Tmds.DBus.Protocol/ProtocolException.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
namespace Tmds.DBus.Protocol;

/// <summary>
/// Exception thrown when an unexpected condition occurs while handling D-Bus messages.
/// Exception thrown when the peer gives an unexpected response.
/// </summary>
public class ProtocolException : Exception
{
Expand Down
4 changes: 2 additions & 2 deletions src/Tmds.DBus.Protocol/Reader.Array.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,9 +109,9 @@ private unsafe T[] ReadArrayOfNumeric<T>() where T : unmanaged
bool dataRead = _reader.TryCopyTo(MemoryMarshal.AsBytes(array.AsSpan()));
if (!dataRead)
{
ThrowHelper.ThrowIndexOutOfRange();
ThrowHelper.ThrowReaderUnexpectedEndOfData();
}
_reader.Advance(sizeof(T) * array.Length);
_reader.Advance(sizeof(T) * array.Length); // TryCopyTo succeeded, data is available
if (sizeof(T) > 1 && ReverseEndianness)
{
#if NET8_0_OR_GREATER
Expand Down
47 changes: 37 additions & 10 deletions src/Tmds.DBus.Protocol/Reader.Basic.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ public byte ReadByte()
{
if (!_reader.TryRead(out byte b))
{
ThrowHelper.ThrowIndexOutOfRange();
ThrowHelper.ThrowReaderUnexpectedEndOfData();
}
return b;
}
Expand Down Expand Up @@ -37,7 +37,7 @@ public short ReadInt16()
bool dataRead = _isBigEndian ? _reader.TryReadBigEndian(out short rv) : _reader.TryReadLittleEndian(out rv);
if (!dataRead)
{
ThrowHelper.ThrowIndexOutOfRange();
ThrowHelper.ThrowReaderUnexpectedEndOfData();
}
return rv;
}
Expand All @@ -57,7 +57,7 @@ public int ReadInt32()
bool dataRead = _isBigEndian ? _reader.TryReadBigEndian(out int rv) : _reader.TryReadLittleEndian(out rv);
if (!dataRead)
{
ThrowHelper.ThrowIndexOutOfRange();
ThrowHelper.ThrowReaderUnexpectedEndOfData();
}
return rv;
}
Expand All @@ -77,7 +77,7 @@ public long ReadInt64()
bool dataRead = _isBigEndian ? _reader.TryReadBigEndian(out long rv) : _reader.TryReadLittleEndian(out rv);
if (!dataRead)
{
ThrowHelper.ThrowIndexOutOfRange();
ThrowHelper.ThrowReaderUnexpectedEndOfData();
}
return rv;
}
Expand Down Expand Up @@ -136,7 +136,7 @@ public void ReadSignature(ReadOnlySpan<byte> expected)
ReadOnlySpan<byte> signature = ReadSignatureAsSpan();
if (!signature.SequenceEqual(expected))
{
ThrowHelper.ThrowUnexpectedSignature(signature, Encoding.UTF8.GetString(expected));
ThrowHelper.ThrowUnexpectedSignature(signature, ThrowHelper.SignatureToStringNoThrow(expected));
}
}

Expand All @@ -163,12 +163,18 @@ public void ReadSignature(ReadOnlySpan<byte> expected)
/// <summary>
/// Reads a string.
/// </summary>
public string ReadString() => Encoding.UTF8.GetString(ReadSpan());
public string ReadString()
{
return DecodeUTF8(ReadSpan());
}

/// <summary>
/// Reads a signature as a string.
/// </summary>
public string ReadSignatureAsString() => Encoding.UTF8.GetString(ReadSignatureAsSpan());
public string ReadSignatureAsString()
{
return DecodeUTF8(ReadSignatureAsSpan());
}

private ReadOnlySpan<byte> ReadSpan()
{
Expand All @@ -181,21 +187,42 @@ private ReadOnlySpan<byte> ReadSpan(int length)
var span = _reader.UnreadSpan;
if (span.Length >= length)
{
_reader.Advance(length + 1);
_reader.Advance(length + 1); // we verified span has enough data
return span.Slice(0, length);
}
else
{
var buffer = new byte[length];
if (!_reader.TryCopyTo(buffer))
{
ThrowHelper.ThrowIndexOutOfRange();
ThrowHelper.ThrowReaderUnexpectedEndOfData();
}
_reader.Advance(length + 1);
_reader.Advance(length + 1); // TryCopyTo succeeded, data is available
return new ReadOnlySpan<byte>(buffer);
}
}

private bool ReverseEndianness
=> BitConverter.IsLittleEndian != !_isBigEndian;

private static string DecodeUTF8(ReadOnlySpan<byte> bytes)
{
#if NET8_0_OR_GREATER
if (!System.Text.Unicode.Utf8.IsValid(bytes))
{
ThrowHelper.ThrowReaderInvalidUTF8();
}
return Encoding.UTF8.GetString(bytes);
#else
try
{
return Encoding.UTF8.GetString(bytes);
}
catch
{
ThrowHelper.ThrowReaderInvalidUTF8();
return string.Empty;
}
#endif
}
}
8 changes: 6 additions & 2 deletions src/Tmds.DBus.Protocol/Reader.Handle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ public ref partial struct Reader
/// <remarks>
/// A handle can only be read once. Use <see cref="SkipSafeHandle"/> to avoid consuming the handle.
/// </remarks>
/// <exception cref="DBusReaderException">The file descriptor is not present in the message.</exception>
/// <exception cref="InvalidOperationException">The handle was already read.</exception>
public T ReadHandle<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)]T>() where T : SafeHandle, new()
=> ReadHandleGeneric<T>();

Expand All @@ -17,7 +19,7 @@ public ref partial struct Reader
int idx = (int)ReadUInt32();
if (_handles is null)
{
UnixFdCollection.ThrowNoHandles();
ThrowHelper.ThrowReaderNoFileHandle();
}
return _handles.ReadHandleGeneric<T>(idx);
}
Expand All @@ -30,12 +32,14 @@ public ref partial struct Reader
/// To skip reading a handle, call <c>ReadHandle&lt;SkipSafeHandle&gt;()</c>, which will return a disposed <see cref="SkipSafeHandle"/> instance without consuming the underlying handle.
/// The handle is still owned (i.e. Disposed) by the <see cref="Message"/>.
/// </remarks>
/// <exception cref="DBusReaderException">The file descriptor is not present in the message.</exception>
/// <exception cref="InvalidOperationException">The handle was already read.</exception>
public IntPtr ReadHandleRaw()
{
int idx = (int)ReadUInt32();
if (_handles is null)
{
UnixFdCollection.ThrowNoHandles();
ThrowHelper.ThrowReaderNoFileHandle();
}
return _handles.ReadHandleRaw(idx);
}
Expand Down
4 changes: 2 additions & 2 deletions src/Tmds.DBus.Protocol/Reader.Variant.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ private VariantValue ReadVariantValue(byte nesting)
SignatureReader sigReader = new(signature);
if (!sigReader.TryRead(out DBusType type, out ReadOnlySpan<byte> innerSignature))
{
ThrowInvalidSignature($"Invalid variant signature: {Encoding.UTF8.GetString(signature)}");
ThrowInvalidSignature($"Invalid variant signature: {ThrowHelper.SignatureToStringNoThrow(signature)}");
}
return ReadTypeAsVariantValue(type, innerSignature, nesting);
}
Expand Down Expand Up @@ -178,7 +178,7 @@ private VariantValue ReadTypeAsVariantValue(DBusType type, ReadOnlySpan<byte> in

private void ThrowInvalidSignature(string message)
{
throw new ProtocolException(message);
throw new DBusReaderException(message);
}

private static VariantValueType ToVariantValueType(DBusType type, ReadOnlySpan<byte> innerSignature)
Expand Down
19 changes: 15 additions & 4 deletions src/Tmds.DBus.Protocol/Reader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,14 @@ public ref partial struct Reader

internal ReadOnlySequence<byte> UnreadSequence => _reader.Sequence.Slice(_reader.Position);

internal void Advance(long count) => _reader.Advance(count);
internal void Advance(long count)
{
if (_reader.Remaining < count)
{
ThrowHelper.ThrowReaderUnexpectedEndOfData();
}
_reader.Advance(count);
}

internal Reader(bool isBigEndian, ReadOnlySequence<byte> sequence) : this(isBigEndian, sequence, handles: null, 0) { }

Expand All @@ -38,7 +45,7 @@ private void AlignReader(int alignment)
long pad = ProtocolConstants.GetPadding((int)_reader.Consumed, alignment);
if (pad != 0)
{
_reader.Advance(pad);
Advance(pad);
}
}

Expand All @@ -53,6 +60,10 @@ internal ArrayEnd ReadArrayStart(int alignment)
{
uint arrayLength = ReadUInt32();
AlignReader(alignment);
if (_reader.Remaining < arrayLength)
{
ThrowHelper.ThrowReaderUnexpectedEndOfData();
}
int endOfArray = (int)(_reader.Consumed + arrayLength);
return new ArrayEnd(alignment, endOfArray);
}
Expand All @@ -73,7 +84,7 @@ public bool HasNext(ArrayEnd iterator)
int advance = nextElement - consumed;
if (advance != 0)
{
_reader.Advance(advance);
_reader.Advance(advance); // array bounds validated in ReadArrayStart
}
return true;
}
Expand All @@ -85,7 +96,7 @@ public bool HasNext(ArrayEnd iterator)
public void SkipTo(ArrayEnd end)
{
int advance = end.EndOfArray - (int)_reader.Consumed;
_reader.Advance(advance);
_reader.Advance(advance); // array bounds validated in ReadArrayStart
}
}

Expand Down
42 changes: 41 additions & 1 deletion src/Tmds.DBus.Protocol/ThrowHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,63 @@ public static void ThrowIfDisposed(bool condition, object instance)
}
}

[DoesNotReturn]
private static void ThrowObjectDisposedException(object instance)
{
throw new ObjectDisposedException(instance?.GetType().FullName);
}

[DoesNotReturn]
public static void ThrowIndexOutOfRange()
{
throw new IndexOutOfRangeException();
}

[DoesNotReturn]
public static void ThrowNotSupportedException()
{
throw new NotSupportedException();
}

[DoesNotReturn]
internal static void ThrowUnexpectedSignature(ReadOnlySpan<byte> signature, string expected)
{
throw new ProtocolException($"Expected signature '{expected}' does not match actual signature '{Encoding.UTF8.GetString(signature)}'.");
throw new DBusReaderException($"Unexpected signature: expected '{expected}', got '{SignatureToStringNoThrow(signature)}'.");
}

[DoesNotReturn]
internal static void ThrowReaderUnexpectedEndOfData()
{
throw new DBusReaderException("Unexpected end of data.");
}

[DoesNotReturn]
internal static void ThrowReaderInvalidUTF8()
{
throw new DBusReaderException("Invalid UTF-8 sequence.");
}

[DoesNotReturn]
internal static void ThrowReaderNoFileHandle()
{
throw new DBusReaderException("File handle not present.");
}

[DoesNotReturn]
internal static void ThrowHandleAlreadyRead()
{
throw new InvalidOperationException("The handle was already read.");
}

internal static string SignatureToStringNoThrow(ReadOnlySpan<byte> signature)
{
try
{
return Encoding.UTF8.GetString(signature);
}
catch
{
return BitConverter.ToString(signature.ToArray());
}
}
}
Loading
Loading