Skip to content

Commit 51909ee

Browse files
authored
Ensure NamedPipeServerStream is assigned in Windows PowerShell (#955)
* Ensure NamedPipeServerStream is reassigned properly * Refactor code for style and to ensure compiler errors for non-assignment
1 parent 2cfdae7 commit 51909ee

File tree

1 file changed

+104
-98
lines changed

1 file changed

+104
-98
lines changed

src/PowerShellEditorServices.Protocol/MessageProtocol/Channel/NamedPipeServerListener.cs

Lines changed: 104 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -20,28 +20,29 @@ namespace Microsoft.PowerShell.EditorServices.Protocol.MessageProtocol.Channel
2020
public class NamedPipeServerListener : ServerListenerBase<NamedPipeServerChannel>
2121
{
2222
// This int will be casted to a PipeOptions enum that only exists in .NET Core 2.1 and up which is why it's not available to us in .NET Standard.
23-
private const int CurrentUserOnly = 536870912;
23+
private const int CurrentUserOnly = 0x20000000;
2424

2525
// In .NET Framework, NamedPipeServerStream has a constructor that takes in a PipeSecurity object. We will use reflection to call the constructor,
2626
// since .NET Framework doesn't have the `CurrentUserOnly` PipeOption.
2727
// doc: https://docs.microsoft.com/en-us/dotnet/api/system.io.pipes.namedpipeserverstream.-ctor?view=netframework-4.7.2#System_IO_Pipes_NamedPipeServerStream__ctor_System_String_System_IO_Pipes_PipeDirection_System_Int32_System_IO_Pipes_PipeTransmissionMode_System_IO_Pipes_PipeOptions_System_Int32_System_Int32_System_IO_Pipes_PipeSecurity_
28-
private static ConstructorInfo _netFrameworkPipeServerConstructor =
28+
private static readonly ConstructorInfo s_netFrameworkPipeServerConstructor =
2929
typeof(NamedPipeServerStream).GetConstructor(new [] { typeof(string), typeof(PipeDirection), typeof(int), typeof(PipeTransmissionMode), typeof(PipeOptions), typeof(int), typeof(int), typeof(PipeSecurity) });
3030

31-
private ILogger logger;
32-
private string inOutPipeName;
33-
private readonly string outPipeName;
34-
private NamedPipeServerStream inOutPipeServer;
35-
private NamedPipeServerStream outPipeServer;
31+
private readonly ILogger _logger;
32+
private readonly string _inOutPipeName;
33+
private readonly string _outPipeName;
34+
35+
private NamedPipeServerStream _inOutPipeServer;
36+
private NamedPipeServerStream _outPipeServer;
3637

3738
public NamedPipeServerListener(
3839
MessageProtocolType messageProtocolType,
3940
string inOutPipeName,
4041
ILogger logger)
4142
: base(messageProtocolType)
4243
{
43-
this.logger = logger;
44-
this.inOutPipeName = inOutPipeName;
44+
_logger = logger;
45+
_inOutPipeName = inOutPipeName;
4546
}
4647

4748
public NamedPipeServerListener(
@@ -51,88 +52,21 @@ public NamedPipeServerListener(
5152
ILogger logger)
5253
: base(messageProtocolType)
5354
{
54-
this.logger = logger;
55-
this.inOutPipeName = inPipeName;
56-
this.outPipeName = outPipeName;
55+
_logger = logger;
56+
_inOutPipeName = inPipeName;
57+
_outPipeName = outPipeName;
5758
}
5859

5960
public override void Start()
6061
{
6162
try
6263
{
63-
// If we're running in Windows PowerShell, we use the constructor via Reflection
64-
if (RuntimeInformation.FrameworkDescription.StartsWith(".NET Framework"))
65-
{
66-
PipeSecurity pipeSecurity = new PipeSecurity();
67-
68-
WindowsIdentity identity = WindowsIdentity.GetCurrent();
69-
WindowsPrincipal principal = new WindowsPrincipal(identity);
70-
71-
if (principal.IsInRole(WindowsBuiltInRole.Administrator))
72-
{
73-
// Allow the Administrators group full access to the pipe.
74-
pipeSecurity.AddAccessRule(new PipeAccessRule(
75-
new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null).Translate(typeof(NTAccount)),
76-
PipeAccessRights.FullControl, AccessControlType.Allow));
77-
}
78-
else
79-
{
80-
// Allow the current user read/write access to the pipe.
81-
pipeSecurity.AddAccessRule(new PipeAccessRule(
82-
WindowsIdentity.GetCurrent().User,
83-
PipeAccessRights.ReadWrite, AccessControlType.Allow));
84-
}
85-
86-
_netFrameworkPipeServerConstructor.Invoke(new object[]
87-
{
88-
inOutPipeName,
89-
PipeDirection.InOut,
90-
1, // maxNumberOfServerInstances
91-
PipeTransmissionMode.Byte,
92-
PipeOptions.Asynchronous,
93-
1024, // inBufferSize
94-
1024, // outBufferSize
95-
pipeSecurity
96-
});
97-
98-
if (this.outPipeName != null)
99-
{
100-
_netFrameworkPipeServerConstructor.Invoke(new object[]
101-
{
102-
outPipeName,
103-
PipeDirection.InOut,
104-
1, // maxNumberOfServerInstances
105-
PipeTransmissionMode.Byte,
106-
PipeOptions.Asynchronous,
107-
1024, // inBufferSize
108-
1024, // outBufferSize
109-
pipeSecurity
110-
});
111-
}
112-
}
113-
else
114-
{
115-
this.inOutPipeServer = new NamedPipeServerStream(
116-
pipeName: inOutPipeName,
117-
direction: PipeDirection.InOut,
118-
maxNumberOfServerInstances: 1,
119-
transmissionMode: PipeTransmissionMode.Byte,
120-
options: PipeOptions.Asynchronous | (PipeOptions)CurrentUserOnly);
121-
if (this.outPipeName != null)
122-
{
123-
this.outPipeServer = new NamedPipeServerStream(
124-
pipeName: outPipeName,
125-
direction: PipeDirection.Out,
126-
maxNumberOfServerInstances: 1,
127-
transmissionMode: PipeTransmissionMode.Byte,
128-
options: (PipeOptions)CurrentUserOnly);
129-
}
130-
}
64+
_inOutPipeServer = ConnectNamedPipe(_inOutPipeName, _outPipeName, out _outPipeServer);
13165
ListenForConnection();
13266
}
13367
catch (IOException e)
13468
{
135-
this.logger.Write(
69+
_logger.Write(
13670
LogLevel.Verbose,
13771
"Named pipe server failed to start due to exception:\r\n\r\n" + e.Message);
13872

@@ -142,22 +76,98 @@ public override void Start()
14276

14377
public override void Stop()
14478
{
145-
if (this.inOutPipeServer != null)
79+
if (_inOutPipeServer != null)
14680
{
147-
this.logger.Write(LogLevel.Verbose, "Named pipe server shutting down...");
81+
_logger.Write(LogLevel.Verbose, "Named pipe server shutting down...");
14882

149-
this.inOutPipeServer.Dispose();
83+
_inOutPipeServer.Dispose();
15084

151-
this.logger.Write(LogLevel.Verbose, "Named pipe server has been disposed.");
85+
_logger.Write(LogLevel.Verbose, "Named pipe server has been disposed.");
15286
}
153-
if (this.outPipeServer != null)
87+
88+
if (_outPipeServer != null)
89+
{
90+
_logger.Write(LogLevel.Verbose, $"Named out pipe server {_outPipeServer} shutting down...");
91+
92+
_outPipeServer.Dispose();
93+
94+
_logger.Write(LogLevel.Verbose, $"Named out pipe server {_outPipeServer} has been disposed.");
95+
}
96+
}
97+
98+
private static NamedPipeServerStream ConnectNamedPipe(
99+
string inOutPipeName,
100+
string outPipeName,
101+
out NamedPipeServerStream outPipe)
102+
{
103+
// .NET Core implementation is simplest so try that first
104+
if (Utils.IsNetCore)
154105
{
155-
this.logger.Write(LogLevel.Verbose, $"Named out pipe server {outPipeServer} shutting down...");
106+
outPipe = outPipeName == null
107+
? null
108+
: new NamedPipeServerStream(
109+
pipeName: outPipeName,
110+
direction: PipeDirection.Out,
111+
maxNumberOfServerInstances: 1,
112+
transmissionMode: PipeTransmissionMode.Byte,
113+
options: (PipeOptions)CurrentUserOnly);
114+
115+
return new NamedPipeServerStream(
116+
pipeName: inOutPipeName,
117+
direction: PipeDirection.InOut,
118+
maxNumberOfServerInstances: 1,
119+
transmissionMode: PipeTransmissionMode.Byte,
120+
options: PipeOptions.Asynchronous | (PipeOptions)CurrentUserOnly);
121+
}
122+
123+
// Now deal with Windows PowerShell
124+
// We need to use reflection to get a nice constructor
156125

157-
this.outPipeServer.Dispose();
126+
PipeSecurity pipeSecurity = new PipeSecurity();
158127

159-
this.logger.Write(LogLevel.Verbose, $"Named out pipe server {outPipeServer} has been disposed.");
128+
WindowsIdentity identity = WindowsIdentity.GetCurrent();
129+
WindowsPrincipal principal = new WindowsPrincipal(identity);
130+
131+
if (principal.IsInRole(WindowsBuiltInRole.Administrator))
132+
{
133+
// Allow the Administrators group full access to the pipe.
134+
pipeSecurity.AddAccessRule(new PipeAccessRule(
135+
new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null).Translate(typeof(NTAccount)),
136+
PipeAccessRights.FullControl, AccessControlType.Allow));
137+
}
138+
else
139+
{
140+
// Allow the current user read/write access to the pipe.
141+
pipeSecurity.AddAccessRule(new PipeAccessRule(
142+
WindowsIdentity.GetCurrent().User,
143+
PipeAccessRights.ReadWrite, AccessControlType.Allow));
160144
}
145+
146+
outPipe = outPipeName == null
147+
? null
148+
: (NamedPipeServerStream)s_netFrameworkPipeServerConstructor.Invoke(
149+
new object[] {
150+
outPipeName,
151+
PipeDirection.InOut,
152+
1, // maxNumberOfServerInstances
153+
PipeTransmissionMode.Byte,
154+
PipeOptions.Asynchronous,
155+
1024, // inBufferSize
156+
1024, // outBufferSize
157+
pipeSecurity
158+
});
159+
160+
return (NamedPipeServerStream)s_netFrameworkPipeServerConstructor.Invoke(
161+
new object[] {
162+
inOutPipeName,
163+
PipeDirection.InOut,
164+
1, // maxNumberOfServerInstances
165+
PipeTransmissionMode.Byte,
166+
PipeOptions.Asynchronous,
167+
1024, // inBufferSize
168+
1024, // outBufferSize
169+
pipeSecurity
170+
});
161171
}
162172

163173
private void ListenForConnection()
@@ -166,18 +176,18 @@ private void ListenForConnection()
166176
{
167177
try
168178
{
169-
var connectionTasks = new List<Task> {WaitForConnectionAsync(this.inOutPipeServer)};
170-
if (this.outPipeServer != null)
179+
var connectionTasks = new List<Task> {WaitForConnectionAsync(_inOutPipeServer)};
180+
if (_outPipeServer != null)
171181
{
172-
connectionTasks.Add(WaitForConnectionAsync(this.outPipeServer));
182+
connectionTasks.Add(WaitForConnectionAsync(_outPipeServer));
173183
}
174184

175185
await Task.WhenAll(connectionTasks);
176-
this.OnClientConnect(new NamedPipeServerChannel(this.inOutPipeServer, this.outPipeServer, this.logger));
186+
OnClientConnect(new NamedPipeServerChannel(_inOutPipeServer, _outPipeServer, _logger));
177187
}
178188
catch (Exception e)
179189
{
180-
this.logger.WriteException(
190+
_logger.WriteException(
181191
"An unhandled exception occurred while listening for a named pipe client connection",
182192
e);
183193

@@ -188,11 +198,7 @@ private void ListenForConnection()
188198

189199
private static async Task WaitForConnectionAsync(NamedPipeServerStream pipeServerStream)
190200
{
191-
#if CoreCLR
192201
await pipeServerStream.WaitForConnectionAsync();
193-
#else
194-
await Task.Factory.FromAsync(pipeServerStream.BeginWaitForConnection, pipeServerStream.EndWaitForConnection, null);
195-
#endif
196202
await pipeServerStream.FlushAsync();
197203
}
198204
}

0 commit comments

Comments
 (0)