From 6ca147bf996f585931b64177096050d837b9ef1e Mon Sep 17 00:00:00 2001 From: Jeff Handley Date: Wed, 20 Mar 2024 01:24:25 -0700 Subject: [PATCH 1/5] Support setting NamedPipeClientStream.ReadMode with PipeAccessRights ctor overload --- ...tem.IO.Pipes.AccessControl.TypeForwards.cs | 4 ++ .../ref/System.IO.Pipes.AccessControl.cs | 21 ------- .../ref/System.IO.Pipes.AccessControl.csproj | 1 + .../System.IO.Pipes/ref/System.IO.Pipes.cs | 23 +++++++ .../src/Resources/Strings.resx | 3 + .../src/System.IO.Pipes.csproj | 2 +- .../IO/Pipes/NamedPipeClientStream.Unix.cs | 11 ++++ .../IO/Pipes/NamedPipeClientStream.Windows.cs | 62 +++++++++++++++---- .../System/IO/Pipes/NamedPipeClientStream.cs | 2 + .../NamedPipeTest.CreateClient.cs | 28 +++++++++ 10 files changed, 123 insertions(+), 34 deletions(-) create mode 100644 src/libraries/System.IO.Pipes.AccessControl/ref/System.IO.Pipes.AccessControl.TypeForwards.cs diff --git a/src/libraries/System.IO.Pipes.AccessControl/ref/System.IO.Pipes.AccessControl.TypeForwards.cs b/src/libraries/System.IO.Pipes.AccessControl/ref/System.IO.Pipes.AccessControl.TypeForwards.cs new file mode 100644 index 0000000000000..4012db03b63b1 --- /dev/null +++ b/src/libraries/System.IO.Pipes.AccessControl/ref/System.IO.Pipes.AccessControl.TypeForwards.cs @@ -0,0 +1,4 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +[assembly: System.Runtime.CompilerServices.TypeForwardedTo(typeof(System.IO.Pipes.PipeAccessRights))] diff --git a/src/libraries/System.IO.Pipes.AccessControl/ref/System.IO.Pipes.AccessControl.cs b/src/libraries/System.IO.Pipes.AccessControl/ref/System.IO.Pipes.AccessControl.cs index 775004246a5e0..4d35d22a6719a 100644 --- a/src/libraries/System.IO.Pipes.AccessControl/ref/System.IO.Pipes.AccessControl.cs +++ b/src/libraries/System.IO.Pipes.AccessControl/ref/System.IO.Pipes.AccessControl.cs @@ -14,27 +14,6 @@ public static class NamedPipeServerStreamAcl { public static System.IO.Pipes.NamedPipeServerStream Create(string pipeName, System.IO.Pipes.PipeDirection direction, int maxNumberOfServerInstances, System.IO.Pipes.PipeTransmissionMode transmissionMode, System.IO.Pipes.PipeOptions options, int inBufferSize, int outBufferSize, System.IO.Pipes.PipeSecurity? pipeSecurity, System.IO.HandleInheritability inheritability = System.IO.HandleInheritability.None, System.IO.Pipes.PipeAccessRights additionalAccessRights = default) { throw null; } } - [System.FlagsAttribute] - public enum PipeAccessRights - { - ReadData = 1, - WriteData = 2, - CreateNewInstance = 4, - ReadExtendedAttributes = 8, - WriteExtendedAttributes = 16, - ReadAttributes = 128, - WriteAttributes = 256, - Write = 274, - Delete = 65536, - ReadPermissions = 131072, - Read = 131209, - ReadWrite = 131483, - ChangePermissions = 262144, - TakeOwnership = 524288, - Synchronize = 1048576, - FullControl = 2032031, - AccessSystemSecurity = 16777216, - } public sealed partial class PipeAccessRule : System.Security.AccessControl.AccessRule { public PipeAccessRule(System.Security.Principal.IdentityReference identity, System.IO.Pipes.PipeAccessRights rights, System.Security.AccessControl.AccessControlType type) : base (default(System.Security.Principal.IdentityReference), default(int), default(bool), default(System.Security.AccessControl.InheritanceFlags), default(System.Security.AccessControl.PropagationFlags), default(System.Security.AccessControl.AccessControlType)) { } diff --git a/src/libraries/System.IO.Pipes.AccessControl/ref/System.IO.Pipes.AccessControl.csproj b/src/libraries/System.IO.Pipes.AccessControl/ref/System.IO.Pipes.AccessControl.csproj index 24b3086a23d09..d16c599454b65 100644 --- a/src/libraries/System.IO.Pipes.AccessControl/ref/System.IO.Pipes.AccessControl.csproj +++ b/src/libraries/System.IO.Pipes.AccessControl/ref/System.IO.Pipes.AccessControl.csproj @@ -5,6 +5,7 @@ + diff --git a/src/libraries/System.IO.Pipes/ref/System.IO.Pipes.cs b/src/libraries/System.IO.Pipes/ref/System.IO.Pipes.cs index f7b404b93a502..85387dbbd4b63 100644 --- a/src/libraries/System.IO.Pipes/ref/System.IO.Pipes.cs +++ b/src/libraries/System.IO.Pipes/ref/System.IO.Pipes.cs @@ -45,6 +45,8 @@ public sealed partial class NamedPipeClientStream : System.IO.Pipes.PipeStream public NamedPipeClientStream(System.IO.Pipes.PipeDirection direction, bool isAsync, bool isConnected, Microsoft.Win32.SafeHandles.SafePipeHandle safePipeHandle) : base (default(System.IO.Pipes.PipeDirection), default(int)) { } public NamedPipeClientStream(string pipeName) : base (default(System.IO.Pipes.PipeDirection), default(int)) { } public NamedPipeClientStream(string serverName, string pipeName) : base (default(System.IO.Pipes.PipeDirection), default(int)) { } + [System.Runtime.Versioning.SupportedOSPlatformAttribute("windows")] + public NamedPipeClientStream(string serverName, string pipeName, System.IO.Pipes.PipeAccessRights desiredAccessRights, PipeOptions options, System.Security.Principal.TokenImpersonationLevel impersonationLevel, HandleInheritability inheritability) : base(default(System.IO.Pipes.PipeDirection), default(int)) { } public NamedPipeClientStream(string serverName, string pipeName, System.IO.Pipes.PipeDirection direction) : base (default(System.IO.Pipes.PipeDirection), default(int)) { } public NamedPipeClientStream(string serverName, string pipeName, System.IO.Pipes.PipeDirection direction, System.IO.Pipes.PipeOptions options) : base (default(System.IO.Pipes.PipeDirection), default(int)) { } public NamedPipeClientStream(string serverName, string pipeName, System.IO.Pipes.PipeDirection direction, System.IO.Pipes.PipeOptions options, System.Security.Principal.TokenImpersonationLevel impersonationLevel) : base (default(System.IO.Pipes.PipeDirection), default(int)) { } @@ -82,6 +84,27 @@ public void WaitForConnection() { } public System.Threading.Tasks.Task WaitForConnectionAsync() { throw null; } public System.Threading.Tasks.Task WaitForConnectionAsync(System.Threading.CancellationToken cancellationToken) { throw null; } } + [System.FlagsAttribute] + public enum PipeAccessRights + { + ReadData = 1, + WriteData = 2, + CreateNewInstance = 4, + ReadExtendedAttributes = 8, + WriteExtendedAttributes = 16, + ReadAttributes = 128, + WriteAttributes = 256, + Write = 274, + Delete = 65536, + ReadPermissions = 131072, + Read = 131209, + ReadWrite = 131483, + ChangePermissions = 262144, + TakeOwnership = 524288, + Synchronize = 1048576, + FullControl = 2032031, + AccessSystemSecurity = 16777216, + } public enum PipeDirection { In = 1, diff --git a/src/libraries/System.IO.Pipes/src/Resources/Strings.resx b/src/libraries/System.IO.Pipes/src/Resources/Strings.resx index 8910eb4d7ac45..e50c4aa314412 100644 --- a/src/libraries/System.IO.Pipes/src/Resources/Strings.resx +++ b/src/libraries/System.IO.Pipes/src/Resources/Strings.resx @@ -120,6 +120,9 @@ Invalid PipeAccessRights value. + + Specifying PipeAccessRights is not supported on this platform. + This flag may not be set on a pipe. diff --git a/src/libraries/System.IO.Pipes/src/System.IO.Pipes.csproj b/src/libraries/System.IO.Pipes/src/System.IO.Pipes.csproj index d3d1d3152e2e3..b36765a036839 100644 --- a/src/libraries/System.IO.Pipes/src/System.IO.Pipes.csproj +++ b/src/libraries/System.IO.Pipes/src/System.IO.Pipes.csproj @@ -19,6 +19,7 @@ + @@ -116,7 +117,6 @@ - diff --git a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.Unix.cs b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.Unix.cs index 2dc43385bb6ee..4c7df8d63e73a 100644 --- a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.Unix.cs +++ b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.Unix.cs @@ -7,6 +7,7 @@ using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; +using System.Security.Principal; using System.Threading; using Microsoft.Win32.SafeHandles; @@ -18,6 +19,16 @@ namespace System.IO.Pipes /// public sealed partial class NamedPipeClientStream : PipeStream { + [System.Runtime.Versioning.SupportedOSPlatform("windows")] + public NamedPipeClientStream(string serverName, string pipeName, PipeAccessRights desiredAccessRights, + PipeOptions options, TokenImpersonationLevel impersonationLevel, HandleInheritability inheritability) + : base(PipeDirection.InOut, 0) + { + throw new PlatformNotSupportedException(SR.PlatformNotSupported_PipeAccessRights); + } + + private static int AccessRightsFromDirection(PipeDirection _) => 0; + private bool TryConnect(int _ /* timeout */) { // timeout isn't used as Connect will be very fast, diff --git a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.Windows.cs b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.Windows.cs index 6f612bbc904bc..6288f0e2b87b3 100644 --- a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.Windows.cs +++ b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.Windows.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security.Principal; @@ -20,6 +21,53 @@ public sealed partial class NamedPipeClientStream : PipeStream // on the server end, but WaitForConnection will not return until we have returned. Any data written to the // pipe by us after we have connected but before the server has called WaitForConnection will be available // to the server after it calls WaitForConnection. + [System.Runtime.Versioning.SupportedOSPlatform("windows")] + public NamedPipeClientStream(string serverName, string pipeName, PipeAccessRights desiredAccessRights, + PipeOptions options, TokenImpersonationLevel impersonationLevel, HandleInheritability inheritability) + : this(serverName, pipeName, DirectionFromRights(desiredAccessRights), options, impersonationLevel, inheritability) + { + _accessRights = (int)desiredAccessRights; + } + + private static PipeDirection DirectionFromRights(PipeAccessRights desiredAccessRights, [CallerArgumentExpression(nameof(desiredAccessRights))] string? argumentName = null) + { + // 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 new ArgumentOutOfRangeException(argumentName, SR.ArgumentOutOfRange_NeedValidPipeAccessRights); + } + + PipeDirection direction = 0; + + if ((desiredAccessRights & PipeAccessRights.ReadData) != 0) + { + direction |= PipeDirection.In; + } + if ((desiredAccessRights & PipeAccessRights.WriteData) != 0) + { + direction |= PipeDirection.Out; + } + + return direction; + } + + private static int AccessRightsFromDirection(PipeDirection direction) + { + int access = 0; + + if ((PipeDirection.In & direction) != 0) + { + access |= Interop.Kernel32.GenericOperations.GENERIC_READ; + } + if ((PipeDirection.Out & direction) != 0) + { + access |= Interop.Kernel32.GenericOperations.GENERIC_WRITE; + } + + return access; + } + private bool TryConnect(int timeout) { Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs = PipeStream.GetSecAttrs(_inheritability); @@ -34,17 +82,7 @@ private bool TryConnect(int timeout) _pipeFlags |= (((int)_impersonationLevel - 1) << 16); } - int access = 0; - if ((PipeDirection.In & _direction) != 0) - { - access |= Interop.Kernel32.GenericOperations.GENERIC_READ; - } - if ((PipeDirection.Out & _direction) != 0) - { - access |= Interop.Kernel32.GenericOperations.GENERIC_WRITE; - } - - SafePipeHandle handle = CreateNamedPipeClient(_normalizedPipePath, ref secAttrs, _pipeFlags, access); + SafePipeHandle handle = CreateNamedPipeClient(_normalizedPipePath, ref secAttrs, _pipeFlags, _accessRights); if (handle.IsInvalid) { @@ -81,7 +119,7 @@ private bool TryConnect(int timeout) } // Pipe server should be free. Let's try to connect to it. - handle = CreateNamedPipeClient(_normalizedPipePath, ref secAttrs, _pipeFlags, access); + handle = CreateNamedPipeClient(_normalizedPipePath, ref secAttrs, _pipeFlags, _accessRights); if (handle.IsInvalid) { diff --git a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.cs b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.cs index 35681b35b89b0..26db553303171 100644 --- a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.cs +++ b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.cs @@ -24,6 +24,7 @@ public sealed partial class NamedPipeClientStream : PipeStream private readonly PipeOptions _pipeOptions; private readonly HandleInheritability _inheritability; private readonly PipeDirection _direction; + private readonly int _accessRights; // Creates a named pipe client using default server (same machine, or "."), and PipeDirection.InOut public NamedPipeClientStream(string pipeName) @@ -84,6 +85,7 @@ public NamedPipeClientStream(string serverName, string pipeName, PipeDirection d _inheritability = inheritability; _impersonationLevel = impersonationLevel; _pipeOptions = options; + _accessRights = AccessRightsFromDirection(direction); } // Create a NamedPipeClientStream from an existing server pipe handle. diff --git a/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateClient.cs b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateClient.cs index 362ddd80be881..e2e4656bd9f1a 100644 --- a/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateClient.cs +++ b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateClient.cs @@ -17,6 +17,7 @@ public static void NullPipeName_Throws_ArgumentNullException() { AssertExtensions.Throws("pipeName", () => new NamedPipeClientStream(null)); AssertExtensions.Throws("pipeName", () => new NamedPipeClientStream(".", null)); + AssertExtensions.Throws("pipeName", () => new NamedPipeClientStream(".", null, PipeAccessRights.FullControl, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None)); } [Fact] @@ -24,6 +25,7 @@ public static void EmptyStringPipeName_Throws_ArgumentException() { AssertExtensions.Throws("pipeName", () => new NamedPipeClientStream("")); AssertExtensions.Throws("pipeName", () => new NamedPipeClientStream(".", "")); + AssertExtensions.Throws("pipeName", () => new NamedPipeClientStream(".", "", PipeAccessRights.FullControl, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None)); } [Theory] @@ -36,6 +38,7 @@ public static void NullServerName_Throws_ArgumentNullException(PipeDirection dir AssertExtensions.Throws("serverName", () => new NamedPipeClientStream(null, "client1", direction)); AssertExtensions.Throws("serverName", () => new NamedPipeClientStream(null, "client1", direction, PipeOptions.None)); AssertExtensions.Throws("serverName", () => new NamedPipeClientStream(null, "client1", direction, PipeOptions.None, TokenImpersonationLevel.None)); + AssertExtensions.Throws("serverName", () => new NamedPipeClientStream(null, "client1", PipeAccessRights.FullControl, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None)); } [Theory] @@ -48,6 +51,7 @@ public static void EmptyStringServerName_Throws_ArgumentException(PipeDirection AssertExtensions.Throws(null, () => new NamedPipeClientStream("", "client1", direction)); AssertExtensions.Throws(null, () => new NamedPipeClientStream("", "client1", direction, PipeOptions.None)); AssertExtensions.Throws(null, () => new NamedPipeClientStream("", "client1", direction, PipeOptions.None, TokenImpersonationLevel.None)); + AssertExtensions.Throws(null, () => new NamedPipeClientStream("", "client1", PipeAccessRights.FullControl, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None)); } [Theory] @@ -63,6 +67,25 @@ public static void ReservedPipeName_Throws_ArgumentOutOfRangeException(PipeDirec AssertExtensions.Throws("pipeName", () => new NamedPipeClientStream(serverName, reservedName, direction)); AssertExtensions.Throws("pipeName", () => new NamedPipeClientStream(serverName, reservedName, direction, PipeOptions.None)); AssertExtensions.Throws("pipeName", () => new NamedPipeClientStream(serverName, reservedName, direction, PipeOptions.None, TokenImpersonationLevel.Impersonation)); + AssertExtensions.Throws("pipeName", () => new NamedPipeClientStream(serverName, reservedName, PipeAccessRights.FullControl, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None)); + } + + [Fact] + [PlatformSpecific(TestPlatforms.AnyUnix)] + public static void NotSupportedPipeAccessRights_Throws_PlatformNotSupportedException() + { + Assert.Throws(() => new NamedPipeClientStream(".", "client1", PipeAccessRights.FullControl, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None)); + } + + [Theory] + [InlineData(0)] // No bits set + [InlineData(32)] // Invalid bit + [InlineData(33)] // ReadData plus an invalid bit + [InlineData(34)] // WriteData plus an invalid bit + [PlatformSpecific(TestPlatforms.Windows)] + public static void InvalidPipeAccessRights_Throws_ArgumentOutOfRangeException(int rights) + { + AssertExtensions.Throws("desiredAccessRights", () => new NamedPipeClientStream(".", "client1", (PipeAccessRights)rights, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None)); } [Fact] @@ -78,6 +101,7 @@ public static void NotSupportedPipePath_Throws_PlatformNotSupportedException() Assert.Throws(() => new NamedPipeClientStream(hostName, "/tmp/foobar/")); Assert.Throws(() => new NamedPipeClientStream(hostName, "/")); Assert.Throws(() => new NamedPipeClientStream(hostName, "\0")); + Assert.Throws(() => new NamedPipeClientStream(hostName, "\0", PipeAccessRights.FullControl, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None)); } [Theory] @@ -97,6 +121,7 @@ public static void InvalidPipeOptions_Throws_ArgumentOutOfRangeException(PipeDir { AssertExtensions.Throws("options", () => new NamedPipeClientStream(".", "client1", direction, (PipeOptions)255)); AssertExtensions.Throws("options", () => new NamedPipeClientStream(".", "client1", direction, (PipeOptions)255, TokenImpersonationLevel.None)); + AssertExtensions.Throws("options", () => new NamedPipeClientStream(".", "client1", PipeAccessRights.FullControl, (PipeOptions)255, TokenImpersonationLevel.None, HandleInheritability.None)); } [Theory] @@ -106,6 +131,7 @@ public static void InvalidPipeOptions_Throws_ArgumentOutOfRangeException(PipeDir public static void InvalidImpersonationLevel_Throws_ArgumentOutOfRangeException(PipeDirection direction) { AssertExtensions.Throws("impersonationLevel", () => new NamedPipeClientStream(".", "client1", direction, PipeOptions.None, (TokenImpersonationLevel)999)); + AssertExtensions.Throws("impersonationLevel", () => new NamedPipeClientStream(".", "client1", PipeAccessRights.FullControl, PipeOptions.None, (TokenImpersonationLevel)999, HandleInheritability.None)); } [Theory] @@ -159,6 +185,8 @@ public void NamedPipeClientStream_InvalidHandleInerhitability() { AssertExtensions.Throws("inheritability", () => new NamedPipeClientStream("a", "b", PipeDirection.Out, 0, TokenImpersonationLevel.Delegation, HandleInheritability.None - 1)); AssertExtensions.Throws("inheritability", () => new NamedPipeClientStream("a", "b", PipeDirection.Out, 0, TokenImpersonationLevel.Delegation, HandleInheritability.Inheritable + 1)); + AssertExtensions.Throws("inheritability", () => new NamedPipeClientStream("a", "b", PipeAccessRights.FullControl, 0, TokenImpersonationLevel.Delegation, HandleInheritability.None - 1)); + AssertExtensions.Throws("inheritability", () => new NamedPipeClientStream("a", "b", PipeAccessRights.FullControl, 0, TokenImpersonationLevel.Delegation, HandleInheritability.Inheritable + 1)); } } } From d5eacc149076afd03f3d53323be1602da2dc694e Mon Sep 17 00:00:00 2001 From: Jeff Handley Date: Wed, 20 Mar 2024 02:24:24 -0700 Subject: [PATCH 2/5] Add API doc comments for new constructor (and fix placement of comment) --- .../IO/Pipes/NamedPipeClientStream.Windows.cs | 33 ++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.Windows.cs b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.Windows.cs index 6288f0e2b87b3..8ff96fdc35ed4 100644 --- a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.Windows.cs +++ b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.Windows.cs @@ -17,10 +17,31 @@ namespace System.IO.Pipes /// public sealed partial class NamedPipeClientStream : PipeStream { - // Waits for a pipe instance to become available. This method may return before WaitForConnection is called - // on the server end, but WaitForConnection will not return until we have returned. Any data written to the - // pipe by us after we have connected but before the server has called WaitForConnection will be available - // to the server after it calls WaitForConnection. + /// + /// Initializes a new instance of the class with the specified pipe and server names, + /// the desired , and the specified impersonation level and inheritability. + /// + /// The name of the remote computer to connect to, or "." to specify the local computer. + /// The name of the pipe. + /// One of the enumeration values that specifies the desired access rights of the pipe. + /// One of the enumeration values that determines how to open or create the pipe. + /// One of the enumeration values that determines the security impersonation level. + /// One of the enumeration values that determines whether the underlying handle will be inheritable by child processes. + /// or is null. + /// or is a zero-length string. + /// is set to "anonymous". + /// is not a valid value. + /// is not a valid value. + /// is not a valid value. + /// is not a valid value. + /// + /// The pipe direction for this constructor is determined by the parameter. + /// If the parameter specifies , + /// the pipe direction is . If the parameter + /// specifies , the pipe direction is . + /// If the value of specifies both + /// and , the pipe direction is . + /// [System.Runtime.Versioning.SupportedOSPlatform("windows")] public NamedPipeClientStream(string serverName, string pipeName, PipeAccessRights desiredAccessRights, PipeOptions options, TokenImpersonationLevel impersonationLevel, HandleInheritability inheritability) @@ -68,6 +89,10 @@ private static int AccessRightsFromDirection(PipeDirection direction) return access; } + // Waits for a pipe instance to become available. This method may return before WaitForConnection is called + // on the server end, but WaitForConnection will not return until we have returned. Any data written to the + // pipe by us after we have connected but before the server has called WaitForConnection will be available + // to the server after it calls WaitForConnection. private bool TryConnect(int timeout) { Interop.Kernel32.SECURITY_ATTRIBUTES secAttrs = PipeStream.GetSecAttrs(_inheritability); From 5bdf1c858a4ad483a6653e62886847b2f0feee34 Mon Sep 17 00:00:00 2001 From: Jeff Handley Date: Thu, 21 Mar 2024 18:09:57 -0700 Subject: [PATCH 3/5] Add more tests including functional tests. Fill an argument validation gap. --- .../IO/Pipes/NamedPipeClientStream.Windows.cs | 5 +- .../NamedPipeTest.CreateClient.cs | 58 +++++++++- .../NamedPipeTest.MessageMode.Windows.cs | 106 ++++++++++++++++++ .../tests/System.IO.Pipes.Tests.csproj | 1 + 4 files changed, 164 insertions(+), 6 deletions(-) create mode 100644 src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.MessageMode.Windows.cs diff --git a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.Windows.cs b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.Windows.cs index 8ff96fdc35ed4..7ffb560a35900 100644 --- a/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.Windows.cs +++ b/src/libraries/System.IO.Pipes/src/System/IO/Pipes/NamedPipeClientStream.Windows.cs @@ -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); } diff --git a/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateClient.cs b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateClient.cs index e2e4656bd9f1a..c4c1c30ceb832 100644 --- a/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateClient.cs +++ b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateClient.cs @@ -17,6 +17,12 @@ public static void NullPipeName_Throws_ArgumentNullException() { AssertExtensions.Throws("pipeName", () => new NamedPipeClientStream(null)); AssertExtensions.Throws("pipeName", () => new NamedPipeClientStream(".", null)); + } + + [Fact] + [PlatformSpecific(TestPlatforms.Windows)] + public static void NullPipeName_Throws_ArgumentNullException_WithAccessRights() + { AssertExtensions.Throws("pipeName", () => new NamedPipeClientStream(".", null, PipeAccessRights.FullControl, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None)); } @@ -25,6 +31,12 @@ public static void EmptyStringPipeName_Throws_ArgumentException() { AssertExtensions.Throws("pipeName", () => new NamedPipeClientStream("")); AssertExtensions.Throws("pipeName", () => new NamedPipeClientStream(".", "")); + } + + [Fact] + [PlatformSpecific(TestPlatforms.Windows)] + public static void EmptyStringPipeName_Throws_ArgumentException_WithAccessRights() + { AssertExtensions.Throws("pipeName", () => new NamedPipeClientStream(".", "", PipeAccessRights.FullControl, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None)); } @@ -38,6 +50,12 @@ public static void NullServerName_Throws_ArgumentNullException(PipeDirection dir AssertExtensions.Throws("serverName", () => new NamedPipeClientStream(null, "client1", direction)); AssertExtensions.Throws("serverName", () => new NamedPipeClientStream(null, "client1", direction, PipeOptions.None)); AssertExtensions.Throws("serverName", () => new NamedPipeClientStream(null, "client1", direction, PipeOptions.None, TokenImpersonationLevel.None)); + } + + [Fact] + [PlatformSpecific(TestPlatforms.Windows)] + public static void NullServerName_Throws_ArgumentNullException_WithAccessRights() + { AssertExtensions.Throws("serverName", () => new NamedPipeClientStream(null, "client1", PipeAccessRights.FullControl, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None)); } @@ -51,6 +69,12 @@ public static void EmptyStringServerName_Throws_ArgumentException(PipeDirection AssertExtensions.Throws(null, () => new NamedPipeClientStream("", "client1", direction)); AssertExtensions.Throws(null, () => new NamedPipeClientStream("", "client1", direction, PipeOptions.None)); AssertExtensions.Throws(null, () => new NamedPipeClientStream("", "client1", direction, PipeOptions.None, TokenImpersonationLevel.None)); + } + + [Fact] + [PlatformSpecific(TestPlatforms.Windows)] + public static void EmptyStringServerName_Throws_ArgumentException_WithAccessRights() + { AssertExtensions.Throws(null, () => new NamedPipeClientStream("", "client1", PipeAccessRights.FullControl, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None)); } @@ -67,7 +91,13 @@ public static void ReservedPipeName_Throws_ArgumentOutOfRangeException(PipeDirec AssertExtensions.Throws("pipeName", () => new NamedPipeClientStream(serverName, reservedName, direction)); AssertExtensions.Throws("pipeName", () => new NamedPipeClientStream(serverName, reservedName, direction, PipeOptions.None)); AssertExtensions.Throws("pipeName", () => new NamedPipeClientStream(serverName, reservedName, direction, PipeOptions.None, TokenImpersonationLevel.Impersonation)); - AssertExtensions.Throws("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("pipeName", () => new NamedPipeClientStream(".", "anonymous", PipeAccessRights.FullControl, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None)); } [Fact] @@ -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) { @@ -101,7 +132,6 @@ public static void NotSupportedPipePath_Throws_PlatformNotSupportedException() Assert.Throws(() => new NamedPipeClientStream(hostName, "/tmp/foobar/")); Assert.Throws(() => new NamedPipeClientStream(hostName, "/")); Assert.Throws(() => new NamedPipeClientStream(hostName, "\0")); - Assert.Throws(() => new NamedPipeClientStream(hostName, "\0", PipeAccessRights.FullControl, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None)); } [Theory] @@ -121,6 +151,12 @@ public static void InvalidPipeOptions_Throws_ArgumentOutOfRangeException(PipeDir { AssertExtensions.Throws("options", () => new NamedPipeClientStream(".", "client1", direction, (PipeOptions)255)); AssertExtensions.Throws("options", () => new NamedPipeClientStream(".", "client1", direction, (PipeOptions)255, TokenImpersonationLevel.None)); + } + + [Fact] + [PlatformSpecific(TestPlatforms.Windows)] + public static void InvalidPipeOptions_Throws_ArgumentOutOfRangeException_WithAccessRights() + { AssertExtensions.Throws("options", () => new NamedPipeClientStream(".", "client1", PipeAccessRights.FullControl, (PipeOptions)255, TokenImpersonationLevel.None, HandleInheritability.None)); } @@ -131,6 +167,12 @@ public static void InvalidPipeOptions_Throws_ArgumentOutOfRangeException(PipeDir public static void InvalidImpersonationLevel_Throws_ArgumentOutOfRangeException(PipeDirection direction) { AssertExtensions.Throws("impersonationLevel", () => new NamedPipeClientStream(".", "client1", direction, PipeOptions.None, (TokenImpersonationLevel)999)); + } + + [Fact] + [PlatformSpecific(TestPlatforms.Windows)] + public static void InvalidImpersonationLevel_Throws_ArgumentOutOfRangeException_WithAccessRights() + { AssertExtensions.Throws("impersonationLevel", () => new NamedPipeClientStream(".", "client1", PipeAccessRights.FullControl, PipeOptions.None, (TokenImpersonationLevel)999, HandleInheritability.None)); } @@ -181,10 +223,16 @@ public static void BadHandleKind_Throws_IOException(PipeDirection direction) } [Fact] - public void NamedPipeClientStream_InvalidHandleInerhitability() + public static void NamedPipeClientStream_InvalidHandleInerhitability() { AssertExtensions.Throws("inheritability", () => new NamedPipeClientStream("a", "b", PipeDirection.Out, 0, TokenImpersonationLevel.Delegation, HandleInheritability.None - 1)); AssertExtensions.Throws("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("inheritability", () => new NamedPipeClientStream("a", "b", PipeAccessRights.FullControl, 0, TokenImpersonationLevel.Delegation, HandleInheritability.None - 1)); AssertExtensions.Throws("inheritability", () => new NamedPipeClientStream("a", "b", PipeAccessRights.FullControl, 0, TokenImpersonationLevel.Delegation, HandleInheritability.Inheritable + 1)); } diff --git a/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.MessageMode.Windows.cs b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.MessageMode.Windows.cs new file mode 100644 index 0000000000000..9f1e8e94dec93 --- /dev/null +++ b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.MessageMode.Windows.cs @@ -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 }); + + 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; + } + + [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(() => 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; + + Assert.Throws(() => 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; + + Assert.Throws(() => client.ReadMode = PipeTransmissionMode.Message); + } + } +} diff --git a/src/libraries/System.IO.Pipes/tests/System.IO.Pipes.Tests.csproj b/src/libraries/System.IO.Pipes/tests/System.IO.Pipes.Tests.csproj index ef930d859fe19..4225df251c33c 100644 --- a/src/libraries/System.IO.Pipes/tests/System.IO.Pipes.Tests.csproj +++ b/src/libraries/System.IO.Pipes/tests/System.IO.Pipes.Tests.csproj @@ -26,6 +26,7 @@ + From 6d7aad7900110947ee565a0bc7bb111ab29b8759 Mon Sep 17 00:00:00 2001 From: Jeff Handley Date: Thu, 21 Mar 2024 23:30:59 -0700 Subject: [PATCH 4/5] Remove old ApiCompat suppression from PipeAccessRights --- .../System.IO.Pipes/src/CompatibilitySuppressions.xml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/libraries/System.IO.Pipes/src/CompatibilitySuppressions.xml b/src/libraries/System.IO.Pipes/src/CompatibilitySuppressions.xml index a047c21f84fff..db08f006c4c52 100644 --- a/src/libraries/System.IO.Pipes/src/CompatibilitySuppressions.xml +++ b/src/libraries/System.IO.Pipes/src/CompatibilitySuppressions.xml @@ -1,7 +1,6 @@  - CP0001 T:System.IO.Pipes.AnonymousPipeServerStreamAcl @@ -14,12 +13,6 @@ ref/net9.0/System.IO.Pipes.dll runtimes/win/lib/net9.0/System.IO.Pipes.dll - - CP0001 - T:System.IO.Pipes.PipeAccessRights - ref/net9.0/System.IO.Pipes.dll - runtimes/win/lib/net9.0/System.IO.Pipes.dll - CP0001 T:System.IO.Pipes.PipeAccessRule From 8c0a19e05cc847fac12bac17d06d5038882c7dd0 Mon Sep 17 00:00:00 2001 From: Jeff Handley Date: Mon, 25 Mar 2024 22:25:57 -0700 Subject: [PATCH 5/5] Make new tests more readable per PR feedback --- .../NamedPipeTest.CreateClient.Unix.cs | 35 +++++++ .../NamedPipeTest.CreateClient.Windows.cs | 69 ++++++++++++++ .../NamedPipeTest.CreateClient.cs | 93 +------------------ .../NamedPipeTest.MessageMode.Windows.cs | 12 +-- .../tests/System.IO.Pipes.Tests.csproj | 2 + 5 files changed, 111 insertions(+), 100 deletions(-) create mode 100644 src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateClient.Unix.cs create mode 100644 src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateClient.Windows.cs diff --git a/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateClient.Unix.cs b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateClient.Unix.cs new file mode 100644 index 0000000000000..15e84f2ffeff4 --- /dev/null +++ b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateClient.Unix.cs @@ -0,0 +1,35 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Security.Principal; +using Microsoft.Win32.SafeHandles; +using Xunit; + +namespace System.IO.Pipes.Tests +{ + /// + /// Unix-specific tests for the constructors for NamedPipeClientStream + /// + public partial class NamedPipeTest_CreateClient + { + [Fact] + public static void NotSupportedPipeAccessRights_Throws_PlatformNotSupportedException() + { + Assert.Throws(() => new NamedPipeClientStream(".", "client1", PipeAccessRights.FullControl, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None)); + } + + [Fact] + public static void NotSupportedPipePath_Throws_PlatformNotSupportedException() + { + string hostName; + Assert.True(InteropTest.TryGetHostName(out hostName)); + + Assert.Throws(() => new NamedPipeClientStream("foobar" + hostName, "foobar")); + Assert.Throws(() => new NamedPipeClientStream(hostName, "foobar" + Path.GetInvalidFileNameChars()[0])); + Assert.Throws(() => new NamedPipeClientStream(hostName, "/tmp/foo\0bar")); + Assert.Throws(() => new NamedPipeClientStream(hostName, "/tmp/foobar/")); + Assert.Throws(() => new NamedPipeClientStream(hostName, "/")); + Assert.Throws(() => new NamedPipeClientStream(hostName, "\0")); + } + } +} diff --git a/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateClient.Windows.cs b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateClient.Windows.cs new file mode 100644 index 0000000000000..93fef211c8816 --- /dev/null +++ b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateClient.Windows.cs @@ -0,0 +1,69 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Security.Principal; +using Microsoft.Win32.SafeHandles; +using Xunit; + +namespace System.IO.Pipes.Tests +{ + /// + /// Windows-specific tests for the constructors for NamedPipeClientStream + /// + public partial class NamedPipeTest_CreateClient + { + [Fact] + public static void EmptyStringPipeName_Throws_ArgumentException_WithAccessRights() + { + AssertExtensions.Throws("pipeName", () => new NamedPipeClientStream(".", "", PipeAccessRights.FullControl, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None)); + } + + [Fact] + public static void NullServerName_Throws_ArgumentNullException_WithAccessRights() + { + AssertExtensions.Throws("serverName", () => new NamedPipeClientStream(null, "client1", PipeAccessRights.FullControl, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None)); + } + + [Fact] + public static void EmptyStringServerName_Throws_ArgumentException_WithAccessRights() + { + AssertExtensions.Throws(null, () => new NamedPipeClientStream("", "client1", PipeAccessRights.FullControl, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None)); + } + + [Fact] + public static void ReservedPipeName_Throws_ArgumentOutOfRangeException_WithAccessRights() + { + AssertExtensions.Throws("pipeName", () => new NamedPipeClientStream(".", "anonymous", PipeAccessRights.FullControl, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None)); + } + + [Theory] + [InlineData(0)] // No bits set + [InlineData(32)] // 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) + public static void InvalidPipeAccessRights_Throws_ArgumentOutOfRangeException(int rights) + { + AssertExtensions.Throws("desiredAccessRights", () => new NamedPipeClientStream(".", "client1", (PipeAccessRights)rights, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None)); + } + + [Fact] + public static void InvalidPipeOptions_Throws_ArgumentOutOfRangeException_WithAccessRights() + { + AssertExtensions.Throws("options", () => new NamedPipeClientStream(".", "client1", PipeAccessRights.FullControl, (PipeOptions)255, TokenImpersonationLevel.None, HandleInheritability.None)); + } + + [Fact] + public static void InvalidImpersonationLevel_Throws_ArgumentOutOfRangeException_WithAccessRights() + { + AssertExtensions.Throws("impersonationLevel", () => new NamedPipeClientStream(".", "client1", PipeAccessRights.FullControl, PipeOptions.None, (TokenImpersonationLevel)999, HandleInheritability.None)); + } + + [Fact] + public static void NamedPipeClientStream_InvalidHandleInerhitability_WithAccessRights() + { + AssertExtensions.Throws("inheritability", () => new NamedPipeClientStream("a", "b", PipeAccessRights.FullControl, 0, TokenImpersonationLevel.Delegation, HandleInheritability.None - 1)); + AssertExtensions.Throws("inheritability", () => new NamedPipeClientStream("a", "b", PipeAccessRights.FullControl, 0, TokenImpersonationLevel.Delegation, HandleInheritability.Inheritable + 1)); + } + } +} diff --git a/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateClient.cs b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateClient.cs index c4c1c30ceb832..5b164b849fac3 100644 --- a/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateClient.cs +++ b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.CreateClient.cs @@ -10,7 +10,7 @@ namespace System.IO.Pipes.Tests /// /// Tests for the constructors for NamedPipeClientStream /// - public class NamedPipeTest_CreateClient + public partial class NamedPipeTest_CreateClient { [Fact] public static void NullPipeName_Throws_ArgumentNullException() @@ -19,13 +19,6 @@ public static void NullPipeName_Throws_ArgumentNullException() AssertExtensions.Throws("pipeName", () => new NamedPipeClientStream(".", null)); } - [Fact] - [PlatformSpecific(TestPlatforms.Windows)] - public static void NullPipeName_Throws_ArgumentNullException_WithAccessRights() - { - AssertExtensions.Throws("pipeName", () => new NamedPipeClientStream(".", null, PipeAccessRights.FullControl, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None)); - } - [Fact] public static void EmptyStringPipeName_Throws_ArgumentException() { @@ -33,13 +26,6 @@ public static void EmptyStringPipeName_Throws_ArgumentException() AssertExtensions.Throws("pipeName", () => new NamedPipeClientStream(".", "")); } - [Fact] - [PlatformSpecific(TestPlatforms.Windows)] - public static void EmptyStringPipeName_Throws_ArgumentException_WithAccessRights() - { - AssertExtensions.Throws("pipeName", () => new NamedPipeClientStream(".", "", PipeAccessRights.FullControl, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None)); - } - [Theory] [InlineData(PipeDirection.In)] [InlineData(PipeDirection.InOut)] @@ -52,13 +38,6 @@ public static void NullServerName_Throws_ArgumentNullException(PipeDirection dir AssertExtensions.Throws("serverName", () => new NamedPipeClientStream(null, "client1", direction, PipeOptions.None, TokenImpersonationLevel.None)); } - [Fact] - [PlatformSpecific(TestPlatforms.Windows)] - public static void NullServerName_Throws_ArgumentNullException_WithAccessRights() - { - AssertExtensions.Throws("serverName", () => new NamedPipeClientStream(null, "client1", PipeAccessRights.FullControl, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None)); - } - [Theory] [InlineData(PipeDirection.In)] [InlineData(PipeDirection.InOut)] @@ -71,13 +50,6 @@ public static void EmptyStringServerName_Throws_ArgumentException(PipeDirection AssertExtensions.Throws(null, () => new NamedPipeClientStream("", "client1", direction, PipeOptions.None, TokenImpersonationLevel.None)); } - [Fact] - [PlatformSpecific(TestPlatforms.Windows)] - public static void EmptyStringServerName_Throws_ArgumentException_WithAccessRights() - { - AssertExtensions.Throws(null, () => new NamedPipeClientStream("", "client1", PipeAccessRights.FullControl, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None)); - } - [Theory] [InlineData(PipeDirection.In)] [InlineData(PipeDirection.InOut)] @@ -93,47 +65,6 @@ public static void ReservedPipeName_Throws_ArgumentOutOfRangeException(PipeDirec AssertExtensions.Throws("pipeName", () => new NamedPipeClientStream(serverName, reservedName, direction, PipeOptions.None, TokenImpersonationLevel.Impersonation)); } - [Fact] - [PlatformSpecific(TestPlatforms.Windows)] - public static void ReservedPipeName_Throws_ArgumentOutOfRangeException_WithAccessRights() - { - AssertExtensions.Throws("pipeName", () => new NamedPipeClientStream(".", "anonymous", PipeAccessRights.FullControl, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None)); - } - - [Fact] - [PlatformSpecific(TestPlatforms.AnyUnix)] - public static void NotSupportedPipeAccessRights_Throws_PlatformNotSupportedException() - { - Assert.Throws(() => new NamedPipeClientStream(".", "client1", PipeAccessRights.FullControl, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None)); - } - - [Theory] - [InlineData(0)] // No bits set - [InlineData(32)] // 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) - { - AssertExtensions.Throws("desiredAccessRights", () => new NamedPipeClientStream(".", "client1", (PipeAccessRights)rights, PipeOptions.None, TokenImpersonationLevel.None, HandleInheritability.None)); - } - - [Fact] - [PlatformSpecific(TestPlatforms.AnyUnix)] // Not supported pipe path throws PNSE on Unix - public static void NotSupportedPipePath_Throws_PlatformNotSupportedException() - { - string hostName; - Assert.True(InteropTest.TryGetHostName(out hostName)); - - Assert.Throws(() => new NamedPipeClientStream("foobar" + hostName, "foobar")); - Assert.Throws(() => new NamedPipeClientStream(hostName, "foobar" + Path.GetInvalidFileNameChars()[0])); - Assert.Throws(() => new NamedPipeClientStream(hostName, "/tmp/foo\0bar")); - Assert.Throws(() => new NamedPipeClientStream(hostName, "/tmp/foobar/")); - Assert.Throws(() => new NamedPipeClientStream(hostName, "/")); - Assert.Throws(() => new NamedPipeClientStream(hostName, "\0")); - } - [Theory] [InlineData((PipeDirection)123)] public static void InvalidPipeDirection_Throws_ArgumentOutOfRangeException(PipeDirection direction) @@ -153,13 +84,6 @@ public static void InvalidPipeOptions_Throws_ArgumentOutOfRangeException(PipeDir AssertExtensions.Throws("options", () => new NamedPipeClientStream(".", "client1", direction, (PipeOptions)255, TokenImpersonationLevel.None)); } - [Fact] - [PlatformSpecific(TestPlatforms.Windows)] - public static void InvalidPipeOptions_Throws_ArgumentOutOfRangeException_WithAccessRights() - { - AssertExtensions.Throws("options", () => new NamedPipeClientStream(".", "client1", PipeAccessRights.FullControl, (PipeOptions)255, TokenImpersonationLevel.None, HandleInheritability.None)); - } - [Theory] [InlineData(PipeDirection.In)] [InlineData(PipeDirection.InOut)] @@ -169,13 +93,6 @@ public static void InvalidImpersonationLevel_Throws_ArgumentOutOfRangeException( AssertExtensions.Throws("impersonationLevel", () => new NamedPipeClientStream(".", "client1", direction, PipeOptions.None, (TokenImpersonationLevel)999)); } - [Fact] - [PlatformSpecific(TestPlatforms.Windows)] - public static void InvalidImpersonationLevel_Throws_ArgumentOutOfRangeException_WithAccessRights() - { - AssertExtensions.Throws("impersonationLevel", () => new NamedPipeClientStream(".", "client1", PipeAccessRights.FullControl, PipeOptions.None, (TokenImpersonationLevel)999, HandleInheritability.None)); - } - [Theory] [InlineData(PipeDirection.In)] [InlineData(PipeDirection.InOut)] @@ -228,13 +145,5 @@ public static void NamedPipeClientStream_InvalidHandleInerhitability() AssertExtensions.Throws("inheritability", () => new NamedPipeClientStream("a", "b", PipeDirection.Out, 0, TokenImpersonationLevel.Delegation, HandleInheritability.None - 1)); AssertExtensions.Throws("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("inheritability", () => new NamedPipeClientStream("a", "b", PipeAccessRights.FullControl, 0, TokenImpersonationLevel.Delegation, HandleInheritability.None - 1)); - AssertExtensions.Throws("inheritability", () => new NamedPipeClientStream("a", "b", PipeAccessRights.FullControl, 0, TokenImpersonationLevel.Delegation, HandleInheritability.Inheritable + 1)); - } } } diff --git a/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.MessageMode.Windows.cs b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.MessageMode.Windows.cs index 9f1e8e94dec93..ff28c95855873 100644 --- a/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.MessageMode.Windows.cs +++ b/src/libraries/System.IO.Pipes/tests/NamedPipeTests/NamedPipeTest.MessageMode.Windows.cs @@ -73,22 +73,20 @@ public void ServerIn_ClientConnect_Throws(PipeTransmissionMode serverMode, PipeO [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) + public void 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; + Task.WaitAll(server.WaitForConnectionAsync(), client.ConnectAsync()); Assert.Throws(() => client.ReadMode = PipeTransmissionMode.Message); } [Fact] - public async Task PipeAccessRights_Without_WriteAttributes_ClientReadModeMessage_Throws() + public void PipeAccessRights_Without_WriteAttributes_ClientReadModeMessage_Throws() { string pipeName = PipeStreamConformanceTests.GetUniquePipeName(); PipeAccessRights rights = MinimumMessageAccessRights & ~PipeAccessRights.WriteAttributes; @@ -96,9 +94,7 @@ public async Task PipeAccessRights_Without_WriteAttributes_ClientReadModeMessage 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; + Task.WaitAll(server.WaitForConnectionAsync(), client.ConnectAsync()); Assert.Throws(() => client.ReadMode = PipeTransmissionMode.Message); } diff --git a/src/libraries/System.IO.Pipes/tests/System.IO.Pipes.Tests.csproj b/src/libraries/System.IO.Pipes/tests/System.IO.Pipes.Tests.csproj index 4225df251c33c..6d498058d8315 100644 --- a/src/libraries/System.IO.Pipes/tests/System.IO.Pipes.Tests.csproj +++ b/src/libraries/System.IO.Pipes/tests/System.IO.Pipes.Tests.csproj @@ -25,6 +25,7 @@ + @@ -37,6 +38,7 @@ +