Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support setting NamedPipeClientStream.ReadMode with PipeAccessRights ctor overload #100001

Merged
merged 7 commits into from
Mar 27, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ private static PipeDirection DirectionFromRights(PipeAccessRights desiredAccessR
{
// Validate the desiredAccessRights parameter here to ensure an invalid value does not result
// in an argument exception being thrown for the direction argument
if (desiredAccessRights == 0 || (desiredAccessRights & ~(PipeAccessRights.FullControl | PipeAccessRights.AccessSystemSecurity)) != 0)
// Throw if there are any unrecognized bits
// Throw if neither ReadData nor WriteData are specified, as this will result in an invalid PipeDirection
if ((desiredAccessRights & ~(PipeAccessRights.FullControl | PipeAccessRights.AccessSystemSecurity)) != 0 ||
((desiredAccessRights & (PipeAccessRights.ReadData | PipeAccessRights.WriteData)) == 0))
{
throw new ArgumentOutOfRangeException(argumentName, SR.ArgumentOutOfRange_NeedValidPipeAccessRights);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,12 @@ public static void NullPipeName_Throws_ArgumentNullException()
{
AssertExtensions.Throws<ArgumentNullException>("pipeName", () => new NamedPipeClientStream(null));
AssertExtensions.Throws<ArgumentNullException>("pipeName", () => new NamedPipeClientStream(".", null));
}

[Fact]
[PlatformSpecific(TestPlatforms.Windows)]
jeffhandley marked this conversation as resolved.
Show resolved Hide resolved
public static void NullPipeName_Throws_ArgumentNullException_WithAccessRights()
{
AssertExtensions.Throws<ArgumentNullException>("pipeName", () => new NamedPipeClientStream(".", null, PipeAccessRights.FullControl, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None));
jeffhandley marked this conversation as resolved.
Show resolved Hide resolved
}

Expand All @@ -25,6 +31,12 @@ public static void EmptyStringPipeName_Throws_ArgumentException()
{
AssertExtensions.Throws<ArgumentException>("pipeName", () => new NamedPipeClientStream(""));
AssertExtensions.Throws<ArgumentException>("pipeName", () => new NamedPipeClientStream(".", ""));
}

[Fact]
[PlatformSpecific(TestPlatforms.Windows)]
public static void EmptyStringPipeName_Throws_ArgumentException_WithAccessRights()
{
AssertExtensions.Throws<ArgumentException>("pipeName", () => new NamedPipeClientStream(".", "", PipeAccessRights.FullControl, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None));
}

Expand All @@ -38,6 +50,12 @@ public static void NullServerName_Throws_ArgumentNullException(PipeDirection dir
AssertExtensions.Throws<ArgumentNullException>("serverName", () => new NamedPipeClientStream(null, "client1", direction));
AssertExtensions.Throws<ArgumentNullException>("serverName", () => new NamedPipeClientStream(null, "client1", direction, PipeOptions.None));
AssertExtensions.Throws<ArgumentNullException>("serverName", () => new NamedPipeClientStream(null, "client1", direction, PipeOptions.None, TokenImpersonationLevel.None));
}

[Fact]
[PlatformSpecific(TestPlatforms.Windows)]
public static void NullServerName_Throws_ArgumentNullException_WithAccessRights()
{
AssertExtensions.Throws<ArgumentNullException>("serverName", () => new NamedPipeClientStream(null, "client1", PipeAccessRights.FullControl, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None));
}

Expand All @@ -51,6 +69,12 @@ public static void EmptyStringServerName_Throws_ArgumentException(PipeDirection
AssertExtensions.Throws<ArgumentException>(null, () => new NamedPipeClientStream("", "client1", direction));
AssertExtensions.Throws<ArgumentException>(null, () => new NamedPipeClientStream("", "client1", direction, PipeOptions.None));
AssertExtensions.Throws<ArgumentException>(null, () => new NamedPipeClientStream("", "client1", direction, PipeOptions.None, TokenImpersonationLevel.None));
}

[Fact]
[PlatformSpecific(TestPlatforms.Windows)]
public static void EmptyStringServerName_Throws_ArgumentException_WithAccessRights()
{
AssertExtensions.Throws<ArgumentException>(null, () => new NamedPipeClientStream("", "client1", PipeAccessRights.FullControl, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None));
}

Expand All @@ -67,7 +91,13 @@ public static void ReservedPipeName_Throws_ArgumentOutOfRangeException(PipeDirec
AssertExtensions.Throws<ArgumentOutOfRangeException>("pipeName", () => new NamedPipeClientStream(serverName, reservedName, direction));
AssertExtensions.Throws<ArgumentOutOfRangeException>("pipeName", () => new NamedPipeClientStream(serverName, reservedName, direction, PipeOptions.None));
AssertExtensions.Throws<ArgumentOutOfRangeException>("pipeName", () => new NamedPipeClientStream(serverName, reservedName, direction, PipeOptions.None, TokenImpersonationLevel.Impersonation));
AssertExtensions.Throws<ArgumentOutOfRangeException>("pipeName", () => new NamedPipeClientStream(serverName, reservedName, PipeAccessRights.FullControl, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None));
}

[Fact]
[PlatformSpecific(TestPlatforms.Windows)]
public static void ReservedPipeName_Throws_ArgumentOutOfRangeException_WithAccessRights()
{
AssertExtensions.Throws<ArgumentOutOfRangeException>("pipeName", () => new NamedPipeClientStream(".", "anonymous", PipeAccessRights.FullControl, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None));
}

[Fact]
Expand All @@ -80,8 +110,9 @@ public static void NotSupportedPipeAccessRights_Throws_PlatformNotSupportedExcep
[Theory]
[InlineData(0)] // No bits set
[InlineData(32)] // Invalid bit
[InlineData(33)] // ReadData plus an invalid bit
[InlineData(34)] // WriteData plus an invalid bit
[InlineData(32 + (int)PipeAccessRights.ReadData)] // ReadData plus an invalid bit
[InlineData(32 + (int)PipeAccessRights.WriteData)] // WriteData plus an invalid bit
[InlineData((int)PipeAccessRights.WriteAttributes)] // Missing ReadData and WriteData (no direction can be determined)
[PlatformSpecific(TestPlatforms.Windows)]
public static void InvalidPipeAccessRights_Throws_ArgumentOutOfRangeException(int rights)
{
Expand All @@ -101,7 +132,6 @@ public static void NotSupportedPipePath_Throws_PlatformNotSupportedException()
Assert.Throws<PlatformNotSupportedException>(() => new NamedPipeClientStream(hostName, "/tmp/foobar/"));
Assert.Throws<PlatformNotSupportedException>(() => new NamedPipeClientStream(hostName, "/"));
Assert.Throws<PlatformNotSupportedException>(() => new NamedPipeClientStream(hostName, "\0"));
Assert.Throws<PlatformNotSupportedException>(() => new NamedPipeClientStream(hostName, "\0", PipeAccessRights.FullControl, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None));
}

[Theory]
Expand All @@ -121,6 +151,12 @@ public static void InvalidPipeOptions_Throws_ArgumentOutOfRangeException(PipeDir
{
AssertExtensions.Throws<ArgumentOutOfRangeException>("options", () => new NamedPipeClientStream(".", "client1", direction, (PipeOptions)255));
AssertExtensions.Throws<ArgumentOutOfRangeException>("options", () => new NamedPipeClientStream(".", "client1", direction, (PipeOptions)255, TokenImpersonationLevel.None));
}

[Fact]
[PlatformSpecific(TestPlatforms.Windows)]
public static void InvalidPipeOptions_Throws_ArgumentOutOfRangeException_WithAccessRights()
{
AssertExtensions.Throws<ArgumentOutOfRangeException>("options", () => new NamedPipeClientStream(".", "client1", PipeAccessRights.FullControl, (PipeOptions)255, TokenImpersonationLevel.None, HandleInheritability.None));
}

Expand All @@ -131,6 +167,12 @@ public static void InvalidPipeOptions_Throws_ArgumentOutOfRangeException(PipeDir
public static void InvalidImpersonationLevel_Throws_ArgumentOutOfRangeException(PipeDirection direction)
{
AssertExtensions.Throws<ArgumentOutOfRangeException>("impersonationLevel", () => new NamedPipeClientStream(".", "client1", direction, PipeOptions.None, (TokenImpersonationLevel)999));
}

[Fact]
[PlatformSpecific(TestPlatforms.Windows)]
public static void InvalidImpersonationLevel_Throws_ArgumentOutOfRangeException_WithAccessRights()
{
AssertExtensions.Throws<ArgumentOutOfRangeException>("impersonationLevel", () => new NamedPipeClientStream(".", "client1", PipeAccessRights.FullControl, PipeOptions.None, (TokenImpersonationLevel)999, HandleInheritability.None));
}

Expand Down Expand Up @@ -181,10 +223,16 @@ public static void BadHandleKind_Throws_IOException(PipeDirection direction)
}

[Fact]
public void NamedPipeClientStream_InvalidHandleInerhitability()
public static void NamedPipeClientStream_InvalidHandleInerhitability()
{
AssertExtensions.Throws<ArgumentOutOfRangeException>("inheritability", () => new NamedPipeClientStream("a", "b", PipeDirection.Out, 0, TokenImpersonationLevel.Delegation, HandleInheritability.None - 1));
AssertExtensions.Throws<ArgumentOutOfRangeException>("inheritability", () => new NamedPipeClientStream("a", "b", PipeDirection.Out, 0, TokenImpersonationLevel.Delegation, HandleInheritability.Inheritable + 1));
}

[Fact]
[PlatformSpecific(TestPlatforms.Windows)]
public static void NamedPipeClientStream_InvalidHandleInerhitability_WithAccessRights()
{
AssertExtensions.Throws<ArgumentOutOfRangeException>("inheritability", () => new NamedPipeClientStream("a", "b", PipeAccessRights.FullControl, 0, TokenImpersonationLevel.Delegation, HandleInheritability.None - 1));
AssertExtensions.Throws<ArgumentOutOfRangeException>("inheritability", () => new NamedPipeClientStream("a", "b", PipeAccessRights.FullControl, 0, TokenImpersonationLevel.Delegation, HandleInheritability.Inheritable + 1));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using System.IO.Tests;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using Xunit;

namespace System.IO.Pipes.Tests
{
// Support for PipeAccessRights and setting ReadMode to Message is only supported on Windows
public class NamedPipeTest_MessageMode_Windows
{
private const PipeAccessRights MinimumMessageAccessRights = PipeAccessRights.ReadData | PipeAccessRights.WriteAttributes;

private static NamedPipeClientStream CreateClientStream(string pipeName, PipeOptions options) =>
new NamedPipeClientStream(".", pipeName, MinimumMessageAccessRights, options, Security.Principal.TokenImpersonationLevel.None, HandleInheritability.None);

[Theory]
[InlineData(PipeDirection.Out, PipeOptions.None)]
[InlineData(PipeDirection.InOut, PipeOptions.Asynchronous)]
public async Task Client_DetectsMessageCompleted(PipeDirection serverDirection, PipeOptions options)
{
string pipeName = PipeStreamConformanceTests.GetUniquePipeName();

using NamedPipeServerStream server = new NamedPipeServerStream(pipeName, serverDirection, 1, PipeTransmissionMode.Message, options);
using NamedPipeClientStream client = CreateClientStream(pipeName, options);

Task.WaitAll(server.WaitForConnectionAsync(), client.ConnectAsync());
client.ReadMode = PipeTransmissionMode.Message;

ValueTask serverWrite = server.WriteAsync(new byte[] { 1, 2, 3, 4, 5 });
jeffhandley marked this conversation as resolved.
Show resolved Hide resolved

byte[] buffer1 = new byte[2], buffer2 = new byte[2], buffer3 = new byte[2];
bool[] messageCompleted = new bool[3];

int bytesRead = client.Read(buffer1, 0, 2);
messageCompleted[0] = client.IsMessageComplete;

bytesRead += client.Read(buffer2, 0, 2);
messageCompleted[1] = client.IsMessageComplete;

bytesRead += client.Read(buffer3, 0, 2);
messageCompleted[2] = client.IsMessageComplete;

Assert.Equal(5, bytesRead);
Assert.Equal(new byte[] { 1, 2, 3, 4, 5, 0 }, buffer1.Concat(buffer2).Concat(buffer3));
Assert.Equal(new bool[] { false, false, true }, messageCompleted);

await serverWrite;
jeffhandley marked this conversation as resolved.
Show resolved Hide resolved
}

[Theory]
[InlineData(PipeTransmissionMode.Byte, PipeOptions.None)]
[InlineData(PipeTransmissionMode.Message, PipeOptions.None)]
[InlineData(PipeTransmissionMode.Byte, PipeOptions.Asynchronous)]
[InlineData(PipeTransmissionMode.Message, PipeOptions.Asynchronous)]
public void ServerIn_ClientConnect_Throws(PipeTransmissionMode serverMode, PipeOptions options)
{
string pipeName = PipeStreamConformanceTests.GetUniquePipeName();

using NamedPipeServerStream server = new NamedPipeServerStream(pipeName, PipeDirection.In, 1, serverMode, options);
using NamedPipeClientStream client = CreateClientStream(pipeName, options);

Assert.Throws<UnauthorizedAccessException>(() => client.Connect());
}

[Theory]
[InlineData(PipeDirection.Out, PipeTransmissionMode.Byte, PipeOptions.None)]
[InlineData(PipeDirection.Out, PipeTransmissionMode.Byte, PipeOptions.Asynchronous)]
[InlineData(PipeDirection.InOut, PipeTransmissionMode.Byte, PipeOptions.None)]
[InlineData(PipeDirection.InOut, PipeTransmissionMode.Byte, PipeOptions.Asynchronous)]
public async Task ServerByteMode_ClientReadModeMessage_Throws(PipeDirection serverDirection, PipeTransmissionMode serverMode, PipeOptions options)
{
string pipeName = PipeStreamConformanceTests.GetUniquePipeName();

using NamedPipeServerStream server = new NamedPipeServerStream(pipeName, serverDirection, 1, serverMode, options);
using NamedPipeClientStream client = CreateClientStream(pipeName, options);

Task serverConnected = server.WaitForConnectionAsync();
client.Connect();
await serverConnected;
jeffhandley marked this conversation as resolved.
Show resolved Hide resolved

Assert.Throws<IOException>(() => client.ReadMode = PipeTransmissionMode.Message);
}

[Fact]
public async Task PipeAccessRights_Without_WriteAttributes_ClientReadModeMessage_Throws()
{
string pipeName = PipeStreamConformanceTests.GetUniquePipeName();
PipeAccessRights rights = MinimumMessageAccessRights & ~PipeAccessRights.WriteAttributes;

using NamedPipeServerStream server = new NamedPipeServerStream(pipeName, PipeDirection.InOut, 1, PipeTransmissionMode.Message);
using NamedPipeClientStream client = new NamedPipeClientStream(".", pipeName, rights, PipeOptions.None, Security.Principal.TokenImpersonationLevel.None, HandleInheritability.None);

Task serverConnected = server.WaitForConnectionAsync();
client.Connect();
await serverConnected;
jeffhandley marked this conversation as resolved.
Show resolved Hide resolved

Assert.Throws<UnauthorizedAccessException>(() => client.ReadMode = PipeTransmissionMode.Message);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
</ItemGroup>
<ItemGroup Condition="'$(TargetPlatformIdentifier)' == 'windows'">
<Compile Include="NamedPipeTests\NamedPipeTest.CurrentUserOnly.Windows.cs" />
<Compile Include="NamedPipeTests\NamedPipeTest.MessageMode.Windows.cs" />
<Compile Include="NamedPipeTests\NamedPipeTest.RunAsClient.Windows.cs" />
<Compile Include="InteropTest.Windows.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.CancelIoEx.cs" Link="Common\Interop\Windows\Interop.CancelIoEx.cs" />
Expand Down