You've already forked linux-packaging-mono
Rewrite with hard-coded offsets into the PE file format to discern if a binary is PE32 or PE32+, and then to determine if it contains a "CLR Data Directory" entry that looks valid. Tested with PE32 and PE32+ compiled Mono binaries, PE32 and PE32+ native binaries, and a random assortment of garbage files. Former-commit-id: 9e7ac86ec84f653a2f79b87183efd5b0ebda001b
2910 lines
112 KiB
C#
2910 lines
112 KiB
C#
//------------------------------------------------------------
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//------------------------------------------------------------
|
|
|
|
namespace System.ServiceModel.Channels
|
|
{
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using System.Net;
|
|
using System.Runtime;
|
|
using System.Runtime.Diagnostics;
|
|
using System.Runtime.InteropServices;
|
|
using System.Runtime.Versioning;
|
|
using System.Security.AccessControl;
|
|
using System.ComponentModel;
|
|
using System.Security;
|
|
using System.Security.Cryptography;
|
|
using System.Security.Permissions;
|
|
using System.Security.Principal;
|
|
using System.ServiceModel;
|
|
using System.ServiceModel.Activation;
|
|
using System.ServiceModel.Diagnostics;
|
|
using System.ServiceModel.Diagnostics.Application;
|
|
using System.ServiceModel.Security;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using SafeCloseHandle = System.ServiceModel.Activation.SafeCloseHandle;
|
|
|
|
sealed class PipeConnection : IConnection
|
|
{
|
|
// common state
|
|
PipeHandle pipe;
|
|
CloseState closeState;
|
|
bool aborted;
|
|
bool isBoundToCompletionPort;
|
|
bool autoBindToCompletionPort;
|
|
TraceEventType exceptionEventType;
|
|
static byte[] zeroBuffer;
|
|
|
|
// read state
|
|
object readLock = new object();
|
|
bool inReadingState; // This keeps track of the state machine (IConnection interface).
|
|
bool isReadOutstanding; // This tracks whether an actual I/O is pending.
|
|
OverlappedContext readOverlapped;
|
|
byte[] asyncReadBuffer;
|
|
int readBufferSize;
|
|
ManualResetEvent atEOFEvent;
|
|
bool isAtEOF;
|
|
OverlappedIOCompleteCallback onAsyncReadComplete;
|
|
Exception asyncReadException;
|
|
WaitCallback asyncReadCallback;
|
|
object asyncReadCallbackState;
|
|
int asyncBytesRead;
|
|
|
|
// write state
|
|
object writeLock = new object();
|
|
bool inWritingState; // This keeps track of the state machine (IConnection interface).
|
|
bool isWriteOutstanding; // This tracks whether an actual I/O is pending.
|
|
OverlappedContext writeOverlapped;
|
|
Exception asyncWriteException;
|
|
WaitCallback asyncWriteCallback;
|
|
object asyncWriteCallbackState;
|
|
int asyncBytesToWrite;
|
|
bool isShutdownWritten;
|
|
int syncWriteSize;
|
|
byte[] pendingWriteBuffer;
|
|
BufferManager pendingWriteBufferManager;
|
|
OverlappedIOCompleteCallback onAsyncWriteComplete;
|
|
int writeBufferSize;
|
|
|
|
// timeout support
|
|
TimeSpan readTimeout;
|
|
IOThreadTimer readTimer;
|
|
static Action<object> onReadTimeout;
|
|
string timeoutErrorString;
|
|
TransferOperation timeoutErrorTransferOperation;
|
|
TimeSpan writeTimeout;
|
|
IOThreadTimer writeTimer;
|
|
static Action<object> onWriteTimeout;
|
|
|
|
public PipeConnection(PipeHandle pipe, int connectionBufferSize, bool isBoundToCompletionPort, bool autoBindToCompletionPort)
|
|
{
|
|
if (pipe == null)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("pipe");
|
|
if (pipe.IsInvalid)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("pipe");
|
|
|
|
this.closeState = CloseState.Open;
|
|
this.exceptionEventType = TraceEventType.Error;
|
|
this.isBoundToCompletionPort = isBoundToCompletionPort;
|
|
this.autoBindToCompletionPort = autoBindToCompletionPort;
|
|
this.pipe = pipe;
|
|
this.readBufferSize = connectionBufferSize;
|
|
this.writeBufferSize = connectionBufferSize;
|
|
this.readOverlapped = new OverlappedContext();
|
|
this.asyncReadBuffer = DiagnosticUtility.Utility.AllocateByteArray(connectionBufferSize);
|
|
this.writeOverlapped = new OverlappedContext();
|
|
this.atEOFEvent = new ManualResetEvent(false);
|
|
this.onAsyncReadComplete = new OverlappedIOCompleteCallback(OnAsyncReadComplete);
|
|
this.onAsyncWriteComplete = new OverlappedIOCompleteCallback(OnAsyncWriteComplete);
|
|
}
|
|
|
|
public int AsyncReadBufferSize
|
|
{
|
|
get
|
|
{
|
|
return this.readBufferSize;
|
|
}
|
|
}
|
|
|
|
public byte[] AsyncReadBuffer
|
|
{
|
|
get
|
|
{
|
|
return this.asyncReadBuffer;
|
|
}
|
|
}
|
|
|
|
static byte[] ZeroBuffer
|
|
{
|
|
get
|
|
{
|
|
if (PipeConnection.zeroBuffer == null)
|
|
{
|
|
PipeConnection.zeroBuffer = new byte[1];
|
|
}
|
|
return PipeConnection.zeroBuffer;
|
|
}
|
|
}
|
|
|
|
public TraceEventType ExceptionEventType
|
|
{
|
|
get { return this.exceptionEventType; }
|
|
set { this.exceptionEventType = value; }
|
|
}
|
|
|
|
public IPEndPoint RemoteIPEndPoint
|
|
{
|
|
get { return null; }
|
|
}
|
|
|
|
IOThreadTimer ReadTimer
|
|
{
|
|
get
|
|
{
|
|
if (this.readTimer == null)
|
|
{
|
|
if (onReadTimeout == null)
|
|
{
|
|
onReadTimeout = new Action<object>(OnReadTimeout);
|
|
}
|
|
|
|
this.readTimer = new IOThreadTimer(onReadTimeout, this, false);
|
|
}
|
|
|
|
return this.readTimer;
|
|
}
|
|
}
|
|
IOThreadTimer WriteTimer
|
|
{
|
|
get
|
|
{
|
|
if (this.writeTimer == null)
|
|
{
|
|
if (onWriteTimeout == null)
|
|
{
|
|
onWriteTimeout = new Action<object>(OnWriteTimeout);
|
|
}
|
|
|
|
this.writeTimer = new IOThreadTimer(onWriteTimeout, this, false);
|
|
}
|
|
|
|
return this.writeTimer;
|
|
}
|
|
}
|
|
|
|
static void OnReadTimeout(object state)
|
|
{
|
|
PipeConnection thisPtr = (PipeConnection)state;
|
|
thisPtr.Abort(SR.GetString(SR.PipeConnectionAbortedReadTimedOut, thisPtr.readTimeout), TransferOperation.Read);
|
|
}
|
|
|
|
static void OnWriteTimeout(object state)
|
|
{
|
|
PipeConnection thisPtr = (PipeConnection)state;
|
|
thisPtr.Abort(SR.GetString(SR.PipeConnectionAbortedWriteTimedOut, thisPtr.writeTimeout), TransferOperation.Write);
|
|
}
|
|
|
|
public void Abort()
|
|
{
|
|
Abort(null, TransferOperation.Undefined);
|
|
}
|
|
|
|
void Abort(string timeoutErrorString, TransferOperation transferOperation)
|
|
{
|
|
CloseHandle(true, timeoutErrorString, transferOperation);
|
|
}
|
|
|
|
Exception ConvertPipeException(PipeException pipeException, TransferOperation transferOperation)
|
|
{
|
|
return ConvertPipeException(pipeException.Message, pipeException, transferOperation);
|
|
}
|
|
|
|
Exception ConvertPipeException(string exceptionMessage, PipeException pipeException, TransferOperation transferOperation)
|
|
{
|
|
if (this.timeoutErrorString != null)
|
|
{
|
|
if (transferOperation == this.timeoutErrorTransferOperation)
|
|
{
|
|
return new TimeoutException(this.timeoutErrorString, pipeException);
|
|
}
|
|
else
|
|
{
|
|
return new CommunicationException(this.timeoutErrorString, pipeException);
|
|
}
|
|
}
|
|
else if (this.aborted)
|
|
{
|
|
return new CommunicationObjectAbortedException(exceptionMessage, pipeException);
|
|
}
|
|
else
|
|
{
|
|
return new CommunicationException(exceptionMessage, pipeException);
|
|
}
|
|
}
|
|
|
|
[PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
|
|
public unsafe AsyncCompletionResult BeginRead(int offset, int size, TimeSpan timeout,
|
|
WaitCallback callback, object state)
|
|
{
|
|
ConnectionUtilities.ValidateBufferBounds(AsyncReadBuffer, offset, size);
|
|
|
|
lock (readLock)
|
|
{
|
|
try
|
|
{
|
|
ValidateEnterReadingState(true);
|
|
|
|
if (isAtEOF)
|
|
{
|
|
asyncBytesRead = 0;
|
|
asyncReadException = null;
|
|
return AsyncCompletionResult.Completed;
|
|
}
|
|
|
|
if (autoBindToCompletionPort)
|
|
{
|
|
if (!isBoundToCompletionPort)
|
|
{
|
|
lock (writeLock)
|
|
{
|
|
// readLock, writeLock acquired in order to prevent deadlock
|
|
EnsureBoundToCompletionPort();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (this.isReadOutstanding)
|
|
{
|
|
throw Fx.AssertAndThrow("Read I/O already pending when BeginRead called.");
|
|
}
|
|
try
|
|
{
|
|
this.readTimeout = timeout;
|
|
|
|
if (this.readTimeout != TimeSpan.MaxValue)
|
|
{
|
|
this.ReadTimer.Set(this.readTimeout);
|
|
}
|
|
|
|
this.asyncReadCallback = callback;
|
|
this.asyncReadCallbackState = state;
|
|
|
|
this.isReadOutstanding = true;
|
|
this.readOverlapped.StartAsyncOperation(AsyncReadBuffer, this.onAsyncReadComplete, this.isBoundToCompletionPort);
|
|
if (UnsafeNativeMethods.ReadFile(this.pipe.DangerousGetHandle(), this.readOverlapped.BufferPtr + offset, size, IntPtr.Zero, this.readOverlapped.NativeOverlapped) == 0)
|
|
{
|
|
int error = Marshal.GetLastWin32Error();
|
|
if (error != UnsafeNativeMethods.ERROR_IO_PENDING && error != UnsafeNativeMethods.ERROR_MORE_DATA)
|
|
{
|
|
this.isReadOutstanding = false;
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Exceptions.CreateReadException(error));
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
if (!this.isReadOutstanding)
|
|
{
|
|
// Unbind the buffer.
|
|
this.readOverlapped.CancelAsyncOperation();
|
|
|
|
this.asyncReadCallback = null;
|
|
this.asyncReadCallbackState = null;
|
|
this.ReadTimer.Cancel();
|
|
}
|
|
}
|
|
|
|
if (!this.isReadOutstanding)
|
|
{
|
|
int bytesRead;
|
|
Exception readException = Exceptions.GetOverlappedReadException(this.pipe, this.readOverlapped.NativeOverlapped, out bytesRead);
|
|
if (readException != null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(readException);
|
|
}
|
|
asyncBytesRead = bytesRead;
|
|
HandleReadComplete(asyncBytesRead);
|
|
}
|
|
else
|
|
{
|
|
EnterReadingState();
|
|
}
|
|
|
|
return this.isReadOutstanding ? AsyncCompletionResult.Queued : AsyncCompletionResult.Completed;
|
|
}
|
|
catch (PipeException e)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelper(ConvertPipeException(e, TransferOperation.Read), ExceptionEventType);
|
|
}
|
|
}
|
|
}
|
|
|
|
[PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
|
|
public unsafe AsyncCompletionResult BeginWrite(byte[] buffer, int offset, int size, bool immediate, TimeSpan timeout,
|
|
WaitCallback callback, object state)
|
|
{
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
FinishPendingWrite(timeout);
|
|
|
|
ConnectionUtilities.ValidateBufferBounds(buffer, offset, size);
|
|
|
|
if (autoBindToCompletionPort && !isBoundToCompletionPort)
|
|
{
|
|
// Locks must be both taken, and in this order.
|
|
lock (readLock)
|
|
{
|
|
lock (writeLock)
|
|
{
|
|
ValidateEnterWritingState(true);
|
|
|
|
EnsureBoundToCompletionPort();
|
|
}
|
|
}
|
|
}
|
|
|
|
lock (writeLock)
|
|
{
|
|
try
|
|
{
|
|
ValidateEnterWritingState(true);
|
|
|
|
if (this.isWriteOutstanding)
|
|
{
|
|
throw Fx.AssertAndThrow("Write I/O already pending when BeginWrite called.");
|
|
}
|
|
|
|
try
|
|
{
|
|
this.writeTimeout = timeout;
|
|
this.WriteTimer.Set(timeoutHelper.RemainingTime());
|
|
|
|
this.asyncBytesToWrite = size;
|
|
this.asyncWriteException = null;
|
|
this.asyncWriteCallback = callback;
|
|
this.asyncWriteCallbackState = state;
|
|
|
|
this.isWriteOutstanding = true;
|
|
this.writeOverlapped.StartAsyncOperation(buffer, this.onAsyncWriteComplete, this.isBoundToCompletionPort);
|
|
if (UnsafeNativeMethods.WriteFile(this.pipe.DangerousGetHandle(), this.writeOverlapped.BufferPtr + offset, size, IntPtr.Zero, this.writeOverlapped.NativeOverlapped) == 0)
|
|
{
|
|
int error = Marshal.GetLastWin32Error();
|
|
if (error != UnsafeNativeMethods.ERROR_IO_PENDING)
|
|
{
|
|
this.isWriteOutstanding = false;
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Exceptions.CreateWriteException(error));
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
if (!this.isWriteOutstanding)
|
|
{
|
|
// Unbind the buffer.
|
|
this.writeOverlapped.CancelAsyncOperation();
|
|
|
|
this.ResetWriteState();
|
|
this.WriteTimer.Cancel();
|
|
}
|
|
}
|
|
|
|
if (!this.isWriteOutstanding)
|
|
{
|
|
int bytesWritten;
|
|
Exception writeException = Exceptions.GetOverlappedWriteException(this.pipe, this.writeOverlapped.NativeOverlapped, out bytesWritten);
|
|
if (writeException == null && bytesWritten != size)
|
|
{
|
|
writeException = new PipeException(SR.GetString(SR.PipeWriteIncomplete));
|
|
}
|
|
if (writeException != null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(writeException);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
EnterWritingState();
|
|
}
|
|
|
|
return this.isWriteOutstanding ? AsyncCompletionResult.Queued : AsyncCompletionResult.Completed;
|
|
}
|
|
catch (PipeException e)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelper(ConvertPipeException(e, TransferOperation.Write), ExceptionEventType);
|
|
}
|
|
}
|
|
}
|
|
|
|
// CSDMain 112188: Note asyncAndLinger has no effect here. Async pooling for Tcp was
|
|
// added and NamedPipes currently doesn't obey the async model.
|
|
public void Close(TimeSpan timeout, bool asyncAndLinger)
|
|
{
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
FinishPendingWrite(timeout);
|
|
|
|
bool shouldCloseHandle = false;
|
|
try
|
|
{
|
|
bool existingReadIsPending = false;
|
|
bool shouldReadEOF = false;
|
|
bool shouldWriteEOF = false;
|
|
|
|
lock (readLock)
|
|
{
|
|
lock (writeLock)
|
|
{
|
|
if (!isShutdownWritten && inWritingState)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelper(
|
|
new PipeException(SR.GetString(SR.PipeCantCloseWithPendingWrite)), ExceptionEventType);
|
|
}
|
|
|
|
if (closeState == CloseState.Closing || closeState == CloseState.HandleClosed)
|
|
{
|
|
// already closing or closed, so just return
|
|
return;
|
|
}
|
|
|
|
closeState = CloseState.Closing;
|
|
|
|
shouldCloseHandle = true;
|
|
|
|
if (!isAtEOF)
|
|
{
|
|
if (inReadingState)
|
|
{
|
|
existingReadIsPending = true;
|
|
}
|
|
else
|
|
{
|
|
shouldReadEOF = true;
|
|
}
|
|
}
|
|
|
|
if (!isShutdownWritten)
|
|
{
|
|
shouldWriteEOF = true;
|
|
isShutdownWritten = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (shouldWriteEOF)
|
|
{
|
|
StartWriteZero(timeoutHelper.RemainingTime());
|
|
}
|
|
|
|
if (shouldReadEOF)
|
|
{
|
|
StartReadZero();
|
|
}
|
|
|
|
// wait for shutdown write to complete
|
|
try
|
|
{
|
|
WaitForWriteZero(timeoutHelper.RemainingTime(), true);
|
|
}
|
|
catch (TimeoutException e)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelper(
|
|
new TimeoutException(SR.GetString(SR.PipeShutdownWriteError), e), ExceptionEventType);
|
|
}
|
|
|
|
// ensure we have received EOF signal
|
|
if (shouldReadEOF)
|
|
{
|
|
try
|
|
{
|
|
WaitForReadZero(timeoutHelper.RemainingTime(), true);
|
|
}
|
|
catch (TimeoutException e)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelper(
|
|
new TimeoutException(SR.GetString(SR.PipeShutdownReadError), e), ExceptionEventType);
|
|
}
|
|
}
|
|
else if (existingReadIsPending)
|
|
{
|
|
if (!TimeoutHelper.WaitOne(atEOFEvent, timeoutHelper.RemainingTime()))
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelper(
|
|
new TimeoutException(SR.GetString(SR.PipeShutdownReadError)), ExceptionEventType);
|
|
}
|
|
}
|
|
// else we had already seen EOF.
|
|
|
|
// at this point, we may get exceptions if the other side closes the handle first
|
|
try
|
|
{
|
|
// write an ack for eof
|
|
StartWriteZero(timeoutHelper.RemainingTime());
|
|
|
|
// read an ack for eof
|
|
StartReadZero();
|
|
|
|
// wait for write to complete/fail
|
|
WaitForWriteZero(timeoutHelper.RemainingTime(), false);
|
|
|
|
// wait for read to complete/fail
|
|
WaitForReadZero(timeoutHelper.RemainingTime(), false);
|
|
}
|
|
catch (PipeException e)
|
|
{
|
|
if (!IsBrokenPipeError(e.ErrorCode))
|
|
{
|
|
throw;
|
|
}
|
|
DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
|
|
}
|
|
catch (CommunicationException e)
|
|
{
|
|
DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
|
|
}
|
|
catch (TimeoutException e)
|
|
{
|
|
DiagnosticUtility.TraceHandledException(e, TraceEventType.Information);
|
|
}
|
|
}
|
|
catch (TimeoutException e)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelper(
|
|
new TimeoutException(SR.GetString(SR.PipeCloseFailed), e), ExceptionEventType);
|
|
}
|
|
catch (PipeException e)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelper(
|
|
ConvertPipeException(SR.GetString(SR.PipeCloseFailed), e, TransferOperation.Undefined), ExceptionEventType);
|
|
}
|
|
finally
|
|
{
|
|
if (shouldCloseHandle)
|
|
{
|
|
CloseHandle(false, null, TransferOperation.Undefined);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CloseHandle(bool abort, string timeoutErrorString, TransferOperation transferOperation)
|
|
{
|
|
lock (readLock)
|
|
{
|
|
lock (writeLock)
|
|
{
|
|
if (this.closeState == CloseState.HandleClosed)
|
|
{
|
|
return;
|
|
}
|
|
|
|
this.timeoutErrorString = timeoutErrorString;
|
|
this.timeoutErrorTransferOperation = transferOperation;
|
|
this.aborted = abort;
|
|
this.closeState = CloseState.HandleClosed;
|
|
this.pipe.Close();
|
|
this.readOverlapped.FreeOrDefer();
|
|
this.writeOverlapped.FreeOrDefer();
|
|
|
|
if (this.atEOFEvent != null)
|
|
{
|
|
this.atEOFEvent.Close();
|
|
}
|
|
|
|
// This should only do anything in the abort case.
|
|
try
|
|
{
|
|
FinishPendingWrite(TimeSpan.Zero);
|
|
}
|
|
catch (TimeoutException exception)
|
|
{
|
|
if (TD.CloseTimeoutIsEnabled())
|
|
{
|
|
TD.CloseTimeout(exception.Message);
|
|
}
|
|
DiagnosticUtility.TraceHandledException(exception, TraceEventType.Information);
|
|
}
|
|
catch (CommunicationException exception)
|
|
{
|
|
DiagnosticUtility.TraceHandledException(exception, TraceEventType.Information);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (abort)
|
|
{
|
|
TraceEventType traceEventType = TraceEventType.Warning;
|
|
|
|
// we could be timing out a cached connection
|
|
if (this.ExceptionEventType == TraceEventType.Information)
|
|
{
|
|
traceEventType = this.ExceptionEventType;
|
|
}
|
|
|
|
if (DiagnosticUtility.ShouldTrace(traceEventType))
|
|
{
|
|
TraceUtility.TraceEvent(traceEventType, TraceCode.PipeConnectionAbort, SR.GetString(SR.TraceCodePipeConnectionAbort), this);
|
|
}
|
|
}
|
|
}
|
|
|
|
CommunicationException CreatePipeDuplicationFailedException(int win32Error)
|
|
{
|
|
Exception innerException = new PipeException(SR.GetString(SR.PipeDuplicationFailed), win32Error);
|
|
return new CommunicationException(innerException.Message, innerException);
|
|
}
|
|
|
|
public object DuplicateAndClose(int targetProcessId)
|
|
{
|
|
SafeCloseHandle targetProcessHandle = ListenerUnsafeNativeMethods.OpenProcess(ListenerUnsafeNativeMethods.PROCESS_DUP_HANDLE, false, targetProcessId);
|
|
if (targetProcessHandle.IsInvalid)
|
|
{
|
|
targetProcessHandle.SetHandleAsInvalid();
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelper(
|
|
CreatePipeDuplicationFailedException(Marshal.GetLastWin32Error()), ExceptionEventType);
|
|
}
|
|
try
|
|
{
|
|
// no need to close this handle, it's a pseudo handle. expected value is -1.
|
|
IntPtr sourceProcessHandle = ListenerUnsafeNativeMethods.GetCurrentProcess();
|
|
if (sourceProcessHandle == IntPtr.Zero)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelper(
|
|
CreatePipeDuplicationFailedException(Marshal.GetLastWin32Error()), ExceptionEventType);
|
|
}
|
|
IntPtr duplicatedHandle;
|
|
bool success = UnsafeNativeMethods.DuplicateHandle(sourceProcessHandle, this.pipe, targetProcessHandle, out duplicatedHandle, 0, false, UnsafeNativeMethods.DUPLICATE_SAME_ACCESS);
|
|
if (!success)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelper(
|
|
CreatePipeDuplicationFailedException(Marshal.GetLastWin32Error()), ExceptionEventType);
|
|
}
|
|
this.Abort();
|
|
return duplicatedHandle;
|
|
}
|
|
finally
|
|
{
|
|
targetProcessHandle.Close();
|
|
}
|
|
}
|
|
|
|
public object GetCoreTransport()
|
|
{
|
|
return pipe;
|
|
}
|
|
|
|
void EnsureBoundToCompletionPort()
|
|
{
|
|
// Both read and write locks must be acquired before doing this
|
|
if (!isBoundToCompletionPort)
|
|
{
|
|
ThreadPool.BindHandle(this.pipe);
|
|
isBoundToCompletionPort = true;
|
|
}
|
|
}
|
|
|
|
public int EndRead()
|
|
{
|
|
if (asyncReadException != null)
|
|
{
|
|
Exception exceptionToThrow = asyncReadException;
|
|
asyncReadException = null;
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelper(exceptionToThrow, ExceptionEventType);
|
|
}
|
|
return asyncBytesRead;
|
|
}
|
|
|
|
public void EndWrite()
|
|
{
|
|
if (this.asyncWriteException != null)
|
|
{
|
|
Exception exceptionToThrow = this.asyncWriteException;
|
|
this.asyncWriteException = null;
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelper(exceptionToThrow, ExceptionEventType);
|
|
}
|
|
}
|
|
|
|
void EnterReadingState()
|
|
{
|
|
inReadingState = true;
|
|
}
|
|
|
|
void EnterWritingState()
|
|
{
|
|
inWritingState = true;
|
|
}
|
|
|
|
void ExitReadingState()
|
|
{
|
|
inReadingState = false;
|
|
}
|
|
|
|
void ExitWritingState()
|
|
{
|
|
inWritingState = false;
|
|
}
|
|
|
|
void ReadIOCompleted()
|
|
{
|
|
this.readOverlapped.FreeIfDeferred();
|
|
}
|
|
|
|
void WriteIOCompleted()
|
|
{
|
|
this.writeOverlapped.FreeIfDeferred();
|
|
}
|
|
|
|
void FinishPendingWrite(TimeSpan timeout)
|
|
{
|
|
if (this.pendingWriteBuffer == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
byte[] buffer;
|
|
BufferManager bufferManager;
|
|
lock (this.writeLock)
|
|
{
|
|
if (this.pendingWriteBuffer == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
buffer = this.pendingWriteBuffer;
|
|
this.pendingWriteBuffer = null;
|
|
|
|
bufferManager = this.pendingWriteBufferManager;
|
|
this.pendingWriteBufferManager = null;
|
|
}
|
|
|
|
try
|
|
{
|
|
bool success = false;
|
|
try
|
|
{
|
|
WaitForSyncWrite(timeout, true);
|
|
success = true;
|
|
}
|
|
finally
|
|
{
|
|
lock (this.writeLock)
|
|
{
|
|
try
|
|
{
|
|
if (success)
|
|
{
|
|
FinishSyncWrite(true);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
ExitWritingState();
|
|
if (!this.isWriteOutstanding)
|
|
{
|
|
bufferManager.ReturnBuffer(buffer);
|
|
WriteIOCompleted();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (PipeException e)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelper(ConvertPipeException(e, TransferOperation.Write), ExceptionEventType);
|
|
}
|
|
}
|
|
|
|
#if FUTURE
|
|
ulong GetServerPid()
|
|
{
|
|
ulong id;
|
|
#pragma warning suppress 56523 // Microsoft, Win32Exception ctor calls Marshal.GetLastWin32Error()
|
|
if (!UnsafeNativeMethods.GetNamedPipeServerProcessId(pipe, out id))
|
|
{
|
|
Win32Exception e = new Win32Exception();
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(e.Message, e));
|
|
}
|
|
return id;
|
|
}
|
|
|
|
ulong GetClientPid()
|
|
{
|
|
ulong id;
|
|
#pragma warning suppress 56523 // Microsoft, Win32Exception ctor calls Marshal.GetLastWin32Error()
|
|
if (!UnsafeNativeMethods.GetNamedPipeServerProcessId(pipe, out id))
|
|
{
|
|
Win32Exception e = new Win32Exception();
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(e.Message, e));
|
|
}
|
|
return id;
|
|
}
|
|
#endif
|
|
|
|
void HandleReadComplete(int bytesRead)
|
|
{
|
|
if (bytesRead == 0)
|
|
{
|
|
isAtEOF = true;
|
|
atEOFEvent.Set();
|
|
}
|
|
}
|
|
|
|
bool IsBrokenPipeError(int error)
|
|
{
|
|
return error == UnsafeNativeMethods.ERROR_NO_DATA ||
|
|
error == UnsafeNativeMethods.ERROR_BROKEN_PIPE;
|
|
}
|
|
|
|
Exception CreatePipeClosedException(TransferOperation transferOperation)
|
|
{
|
|
return ConvertPipeException(new PipeException(SR.GetString(SR.PipeClosed)), transferOperation);
|
|
}
|
|
|
|
[PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
|
|
unsafe void OnAsyncReadComplete(bool haveResult, int error, int numBytes)
|
|
{
|
|
WaitCallback callback;
|
|
object state;
|
|
|
|
lock (readLock)
|
|
{
|
|
try
|
|
{
|
|
try
|
|
{
|
|
if (this.readTimeout != TimeSpan.MaxValue && !this.ReadTimer.Cancel())
|
|
{
|
|
this.Abort(SR.GetString(SR.PipeConnectionAbortedReadTimedOut, this.readTimeout), TransferOperation.Read);
|
|
}
|
|
|
|
if (this.closeState == CloseState.HandleClosed)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreatePipeClosedException(TransferOperation.Read));
|
|
}
|
|
if (!haveResult)
|
|
{
|
|
if (UnsafeNativeMethods.GetOverlappedResult(this.pipe.DangerousGetHandle(), this.readOverlapped.NativeOverlapped, out numBytes, 0) == 0)
|
|
{
|
|
error = Marshal.GetLastWin32Error();
|
|
}
|
|
else
|
|
{
|
|
error = 0;
|
|
}
|
|
}
|
|
|
|
if (error != 0 && error != UnsafeNativeMethods.ERROR_MORE_DATA)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Exceptions.CreateReadException((int)error));
|
|
}
|
|
this.asyncBytesRead = numBytes;
|
|
HandleReadComplete(this.asyncBytesRead);
|
|
}
|
|
catch (PipeException e)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(ConvertPipeException(e, TransferOperation.Read));
|
|
}
|
|
}
|
|
#pragma warning suppress 56500 // Microsoft, transferring exception to caller
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e))
|
|
{
|
|
throw;
|
|
}
|
|
|
|
this.asyncReadException = e;
|
|
}
|
|
finally
|
|
{
|
|
this.isReadOutstanding = false;
|
|
ReadIOCompleted();
|
|
ExitReadingState();
|
|
callback = this.asyncReadCallback;
|
|
this.asyncReadCallback = null;
|
|
state = this.asyncReadCallbackState;
|
|
this.asyncReadCallbackState = null;
|
|
}
|
|
}
|
|
|
|
callback(state);
|
|
}
|
|
|
|
[PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
|
|
unsafe void OnAsyncWriteComplete(bool haveResult, int error, int numBytes)
|
|
{
|
|
WaitCallback callback;
|
|
object state;
|
|
|
|
Exception writeException = null;
|
|
|
|
this.WriteTimer.Cancel();
|
|
lock (writeLock)
|
|
{
|
|
try
|
|
{
|
|
try
|
|
{
|
|
if (this.closeState == CloseState.HandleClosed)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreatePipeClosedException(TransferOperation.Write));
|
|
}
|
|
if (!haveResult)
|
|
{
|
|
if (UnsafeNativeMethods.GetOverlappedResult(this.pipe.DangerousGetHandle(), this.writeOverlapped.NativeOverlapped, out numBytes, 0) == 0)
|
|
{
|
|
error = Marshal.GetLastWin32Error();
|
|
}
|
|
else
|
|
{
|
|
error = 0;
|
|
}
|
|
}
|
|
|
|
if (error != 0)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Exceptions.CreateWriteException(error));
|
|
}
|
|
else if (numBytes != this.asyncBytesToWrite)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new PipeException(SR.GetString(SR.PipeWriteIncomplete)));
|
|
}
|
|
}
|
|
catch (PipeException e)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelper(ConvertPipeException(e, TransferOperation.Write), ExceptionEventType);
|
|
}
|
|
}
|
|
#pragma warning suppress 56500 // Microsoft, transferring exception to another thread
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e))
|
|
{
|
|
throw;
|
|
}
|
|
|
|
writeException = e;
|
|
}
|
|
finally
|
|
{
|
|
this.isWriteOutstanding = false;
|
|
WriteIOCompleted();
|
|
ExitWritingState();
|
|
this.asyncWriteException = writeException;
|
|
callback = this.asyncWriteCallback;
|
|
state = this.asyncWriteCallbackState;
|
|
this.ResetWriteState();
|
|
}
|
|
}
|
|
|
|
if (callback != null)
|
|
{
|
|
callback(state);
|
|
}
|
|
}
|
|
|
|
[PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
|
|
unsafe public int Read(byte[] buffer, int offset, int size, TimeSpan timeout)
|
|
{
|
|
ConnectionUtilities.ValidateBufferBounds(buffer, offset, size);
|
|
|
|
try
|
|
{
|
|
lock (readLock)
|
|
{
|
|
ValidateEnterReadingState(true);
|
|
if (isAtEOF)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
StartSyncRead(buffer, offset, size);
|
|
EnterReadingState();
|
|
}
|
|
|
|
int bytesRead = -1;
|
|
bool success = false;
|
|
try
|
|
{
|
|
WaitForSyncRead(timeout, true);
|
|
success = true;
|
|
}
|
|
finally
|
|
{
|
|
lock (this.readLock)
|
|
{
|
|
try
|
|
{
|
|
if (success)
|
|
{
|
|
bytesRead = FinishSyncRead(true);
|
|
HandleReadComplete(bytesRead);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
ExitReadingState();
|
|
if (!this.isReadOutstanding)
|
|
{
|
|
ReadIOCompleted();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Fx.Assert(bytesRead >= 0, "Logic error in Read - bytesRead not set.");
|
|
return bytesRead;
|
|
}
|
|
catch (PipeException e)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelper(ConvertPipeException(e, TransferOperation.Read), ExceptionEventType);
|
|
}
|
|
}
|
|
|
|
public void Shutdown(TimeSpan timeout)
|
|
{
|
|
try
|
|
{
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
FinishPendingWrite(timeoutHelper.RemainingTime());
|
|
|
|
lock (writeLock)
|
|
{
|
|
ValidateEnterWritingState(true);
|
|
StartWriteZero(timeoutHelper.RemainingTime());
|
|
isShutdownWritten = true;
|
|
}
|
|
}
|
|
catch (PipeException e)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelper(ConvertPipeException(e, TransferOperation.Undefined), ExceptionEventType);
|
|
}
|
|
}
|
|
|
|
[PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
|
|
unsafe void StartReadZero()
|
|
{
|
|
lock (this.readLock)
|
|
{
|
|
ValidateEnterReadingState(false);
|
|
StartSyncRead(ZeroBuffer, 0, 1);
|
|
EnterReadingState();
|
|
}
|
|
}
|
|
|
|
[PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
|
|
unsafe void StartWriteZero(TimeSpan timeout)
|
|
{
|
|
FinishPendingWrite(timeout);
|
|
|
|
lock (this.writeLock)
|
|
{
|
|
ValidateEnterWritingState(false);
|
|
StartSyncWrite(ZeroBuffer, 0, 0);
|
|
EnterWritingState();
|
|
}
|
|
}
|
|
|
|
void ResetWriteState()
|
|
{
|
|
this.asyncBytesToWrite = -1;
|
|
this.asyncWriteCallback = null;
|
|
this.asyncWriteCallbackState = null;
|
|
}
|
|
|
|
public IAsyncResult BeginValidate(Uri uri, AsyncCallback callback, object state)
|
|
{
|
|
return new CompletedAsyncResult<bool>(true, callback, state);
|
|
}
|
|
|
|
public bool EndValidate(IAsyncResult result)
|
|
{
|
|
return CompletedAsyncResult<bool>.End(result);
|
|
}
|
|
|
|
void WaitForReadZero(TimeSpan timeout, bool traceExceptionsAsErrors)
|
|
{
|
|
bool success = false;
|
|
try
|
|
{
|
|
WaitForSyncRead(timeout, traceExceptionsAsErrors);
|
|
success = true;
|
|
}
|
|
finally
|
|
{
|
|
lock (this.readLock)
|
|
{
|
|
try
|
|
{
|
|
if (success)
|
|
{
|
|
if (FinishSyncRead(traceExceptionsAsErrors) != 0)
|
|
{
|
|
Exception exception = ConvertPipeException(new PipeException(SR.GetString(SR.PipeSignalExpected)), TransferOperation.Read);
|
|
TraceEventType traceEventType = TraceEventType.Information;
|
|
if (traceExceptionsAsErrors)
|
|
{
|
|
traceEventType = TraceEventType.Error;
|
|
}
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelper(exception, traceEventType);
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
ExitReadingState();
|
|
if (!this.isReadOutstanding)
|
|
{
|
|
ReadIOCompleted();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void WaitForWriteZero(TimeSpan timeout, bool traceExceptionsAsErrors)
|
|
{
|
|
bool success = false;
|
|
try
|
|
{
|
|
WaitForSyncWrite(timeout, traceExceptionsAsErrors);
|
|
success = true;
|
|
}
|
|
finally
|
|
{
|
|
lock (this.writeLock)
|
|
{
|
|
try
|
|
{
|
|
if (success)
|
|
{
|
|
FinishSyncWrite(traceExceptionsAsErrors);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
ExitWritingState();
|
|
if (!this.isWriteOutstanding)
|
|
{
|
|
WriteIOCompleted();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Write(byte[] buffer, int offset, int size, bool immediate, TimeSpan timeout)
|
|
{
|
|
WriteHelper(buffer, offset, size, immediate, timeout, ref this.writeOverlapped.Holder[0]);
|
|
}
|
|
|
|
// The holder is a perf optimization that lets us avoid repeatedly indexing into the array.
|
|
[PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
|
|
unsafe void WriteHelper(byte[] buffer, int offset, int size, bool immediate, TimeSpan timeout, ref object holder)
|
|
{
|
|
try
|
|
{
|
|
FinishPendingWrite(timeout);
|
|
|
|
ConnectionUtilities.ValidateBufferBounds(buffer, offset, size);
|
|
|
|
int bytesToWrite = size;
|
|
if (size > this.writeBufferSize)
|
|
{
|
|
size = this.writeBufferSize;
|
|
}
|
|
|
|
while (bytesToWrite > 0)
|
|
{
|
|
lock (this.writeLock)
|
|
{
|
|
ValidateEnterWritingState(true);
|
|
|
|
StartSyncWrite(buffer, offset, size, ref holder);
|
|
EnterWritingState();
|
|
}
|
|
|
|
bool success = false;
|
|
try
|
|
{
|
|
WaitForSyncWrite(timeout, true, ref holder);
|
|
success = true;
|
|
}
|
|
finally
|
|
{
|
|
lock (this.writeLock)
|
|
{
|
|
try
|
|
{
|
|
if (success)
|
|
{
|
|
FinishSyncWrite(true);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
ExitWritingState();
|
|
if (!this.isWriteOutstanding)
|
|
{
|
|
WriteIOCompleted();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bytesToWrite -= size;
|
|
offset += size;
|
|
if (size > bytesToWrite)
|
|
{
|
|
size = bytesToWrite;
|
|
}
|
|
}
|
|
}
|
|
catch (PipeException e)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelper(ConvertPipeException(e, TransferOperation.Write), ExceptionEventType);
|
|
}
|
|
}
|
|
|
|
[PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
|
|
public unsafe void Write(byte[] buffer, int offset, int size, bool immediate, TimeSpan timeout, BufferManager bufferManager)
|
|
{
|
|
bool shouldReturnBuffer = true;
|
|
|
|
try
|
|
{
|
|
if (size > this.writeBufferSize)
|
|
{
|
|
WriteHelper(buffer, offset, size, immediate, timeout, ref this.writeOverlapped.Holder[0]);
|
|
return;
|
|
}
|
|
|
|
FinishPendingWrite(timeout);
|
|
|
|
ConnectionUtilities.ValidateBufferBounds(buffer, offset, size);
|
|
|
|
lock (this.writeLock)
|
|
{
|
|
ValidateEnterWritingState(true);
|
|
|
|
// This method avoids the call to GetOverlappedResult for synchronous completions. Perf?
|
|
bool success = false;
|
|
try
|
|
{
|
|
shouldReturnBuffer = false;
|
|
StartSyncWrite(buffer, offset, size);
|
|
success = true;
|
|
}
|
|
finally
|
|
{
|
|
if (!this.isWriteOutstanding)
|
|
{
|
|
shouldReturnBuffer = true;
|
|
}
|
|
else
|
|
{
|
|
if (success)
|
|
{
|
|
EnterWritingState();
|
|
|
|
Fx.Assert(this.pendingWriteBuffer == null, "Need to pend a write but one's already pending.");
|
|
this.pendingWriteBuffer = buffer;
|
|
this.pendingWriteBufferManager = bufferManager;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (PipeException e)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelper(ConvertPipeException(e, TransferOperation.Write), ExceptionEventType);
|
|
}
|
|
finally
|
|
{
|
|
if (shouldReturnBuffer)
|
|
{
|
|
bufferManager.ReturnBuffer(buffer);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ValidateEnterReadingState(bool checkEOF)
|
|
{
|
|
if (checkEOF)
|
|
{
|
|
if (closeState == CloseState.Closing)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelper(new PipeException(SR.GetString(SR.PipeAlreadyClosing)), ExceptionEventType);
|
|
}
|
|
}
|
|
|
|
if (inReadingState)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelper(new PipeException(SR.GetString(SR.PipeReadPending)), ExceptionEventType);
|
|
}
|
|
|
|
if (closeState == CloseState.HandleClosed)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelper(new PipeException(SR.GetString(SR.PipeClosed)), ExceptionEventType);
|
|
}
|
|
}
|
|
|
|
void ValidateEnterWritingState(bool checkShutdown)
|
|
{
|
|
if (checkShutdown)
|
|
{
|
|
if (isShutdownWritten)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelper(new PipeException(SR.GetString(SR.PipeAlreadyShuttingDown)), ExceptionEventType);
|
|
}
|
|
|
|
if (closeState == CloseState.Closing)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelper(new PipeException(SR.GetString(SR.PipeAlreadyClosing)), ExceptionEventType);
|
|
}
|
|
}
|
|
|
|
if (inWritingState)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelper(new PipeException(SR.GetString(SR.PipeWritePending)), ExceptionEventType);
|
|
}
|
|
|
|
if (closeState == CloseState.HandleClosed)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelper(new PipeException(SR.GetString(SR.PipeClosed)), ExceptionEventType);
|
|
}
|
|
}
|
|
|
|
void StartSyncRead(byte[] buffer, int offset, int size)
|
|
{
|
|
StartSyncRead(buffer, offset, size, ref this.readOverlapped.Holder[0]);
|
|
}
|
|
|
|
[PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
|
|
unsafe void StartSyncRead(byte[] buffer, int offset, int size, ref object holder)
|
|
{
|
|
if (this.isReadOutstanding)
|
|
{
|
|
throw Fx.AssertAndThrow("StartSyncRead called when read I/O was already pending.");
|
|
}
|
|
|
|
try
|
|
{
|
|
this.isReadOutstanding = true;
|
|
this.readOverlapped.StartSyncOperation(buffer, ref holder);
|
|
if (UnsafeNativeMethods.ReadFile(this.pipe.DangerousGetHandle(), this.readOverlapped.BufferPtr + offset, size, IntPtr.Zero, this.readOverlapped.NativeOverlapped) == 0)
|
|
{
|
|
int error = Marshal.GetLastWin32Error();
|
|
if (error != UnsafeNativeMethods.ERROR_IO_PENDING)
|
|
{
|
|
this.isReadOutstanding = false;
|
|
if (error != UnsafeNativeMethods.ERROR_MORE_DATA)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Exceptions.CreateReadException(error));
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
this.isReadOutstanding = false;
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
if (!this.isReadOutstanding)
|
|
{
|
|
this.readOverlapped.CancelSyncOperation(ref holder);
|
|
}
|
|
}
|
|
}
|
|
|
|
[PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
|
|
unsafe void WaitForSyncRead(TimeSpan timeout, bool traceExceptionsAsErrors)
|
|
{
|
|
if (this.isReadOutstanding)
|
|
{
|
|
if (!this.readOverlapped.WaitForSyncOperation(timeout))
|
|
{
|
|
Abort(SR.GetString(SR.PipeConnectionAbortedReadTimedOut, this.readTimeout), TransferOperation.Read);
|
|
|
|
Exception timeoutException = new TimeoutException(SR.GetString(SR.PipeReadTimedOut, timeout));
|
|
TraceEventType traceEventType = TraceEventType.Information;
|
|
if (traceExceptionsAsErrors)
|
|
{
|
|
traceEventType = TraceEventType.Error;
|
|
}
|
|
|
|
// This intentionally doesn't reset isReadOutstanding, because technically it still is, and we need to not free the buffer.
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelper(timeoutException, traceEventType);
|
|
}
|
|
else
|
|
{
|
|
this.isReadOutstanding = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Must be called in a lock.
|
|
[PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
|
|
unsafe int FinishSyncRead(bool traceExceptionsAsErrors)
|
|
{
|
|
int bytesRead = -1;
|
|
Exception readException;
|
|
|
|
if (this.closeState == CloseState.HandleClosed)
|
|
{
|
|
readException = CreatePipeClosedException(TransferOperation.Read);
|
|
}
|
|
else
|
|
{
|
|
readException = Exceptions.GetOverlappedReadException(this.pipe, this.readOverlapped.NativeOverlapped, out bytesRead);
|
|
}
|
|
if (readException != null)
|
|
{
|
|
TraceEventType traceEventType = TraceEventType.Information;
|
|
if (traceExceptionsAsErrors)
|
|
{
|
|
traceEventType = TraceEventType.Error;
|
|
}
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelper(readException, traceEventType);
|
|
}
|
|
|
|
return bytesRead;
|
|
}
|
|
|
|
void StartSyncWrite(byte[] buffer, int offset, int size)
|
|
{
|
|
StartSyncWrite(buffer, offset, size, ref this.writeOverlapped.Holder[0]);
|
|
}
|
|
|
|
[PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
|
|
unsafe void StartSyncWrite(byte[] buffer, int offset, int size, ref object holder)
|
|
{
|
|
if (this.isWriteOutstanding)
|
|
{
|
|
throw Fx.AssertAndThrow("StartSyncWrite called when write I/O was already pending.");
|
|
}
|
|
|
|
try
|
|
{
|
|
this.syncWriteSize = size;
|
|
this.isWriteOutstanding = true;
|
|
this.writeOverlapped.StartSyncOperation(buffer, ref holder);
|
|
if (UnsafeNativeMethods.WriteFile(this.pipe.DangerousGetHandle(), this.writeOverlapped.BufferPtr + offset, size, IntPtr.Zero, this.writeOverlapped.NativeOverlapped) == 0)
|
|
{
|
|
int error = Marshal.GetLastWin32Error();
|
|
if (error != UnsafeNativeMethods.ERROR_IO_PENDING)
|
|
{
|
|
this.isWriteOutstanding = false;
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(Exceptions.CreateWriteException(error));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
this.isWriteOutstanding = false;
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
if (!this.isWriteOutstanding)
|
|
{
|
|
this.writeOverlapped.CancelSyncOperation(ref holder);
|
|
}
|
|
}
|
|
}
|
|
|
|
void WaitForSyncWrite(TimeSpan timeout, bool traceExceptionsAsErrors)
|
|
{
|
|
WaitForSyncWrite(timeout, traceExceptionsAsErrors, ref this.writeOverlapped.Holder[0]);
|
|
}
|
|
|
|
[PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
|
|
unsafe void WaitForSyncWrite(TimeSpan timeout, bool traceExceptionsAsErrors, ref object holder)
|
|
{
|
|
if (this.isWriteOutstanding)
|
|
{
|
|
if (!this.writeOverlapped.WaitForSyncOperation(timeout, ref holder))
|
|
{
|
|
Abort(SR.GetString(SR.PipeConnectionAbortedWriteTimedOut, this.writeTimeout), TransferOperation.Write);
|
|
|
|
Exception timeoutException = new TimeoutException(SR.GetString(SR.PipeWriteTimedOut, timeout));
|
|
TraceEventType traceEventType = TraceEventType.Information;
|
|
if (traceExceptionsAsErrors)
|
|
{
|
|
traceEventType = TraceEventType.Error;
|
|
}
|
|
|
|
// This intentionally doesn't reset isWriteOutstanding, because technically it still is, and we need to not free the buffer.
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelper(timeoutException, traceEventType);
|
|
}
|
|
else
|
|
{
|
|
this.isWriteOutstanding = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Must be called in a lock.
|
|
[PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
|
|
unsafe void FinishSyncWrite(bool traceExceptionsAsErrors)
|
|
{
|
|
int bytesWritten;
|
|
Exception writeException;
|
|
|
|
if (this.closeState == CloseState.HandleClosed)
|
|
{
|
|
writeException = CreatePipeClosedException(TransferOperation.Write);
|
|
}
|
|
else
|
|
{
|
|
writeException = Exceptions.GetOverlappedWriteException(this.pipe, this.writeOverlapped.NativeOverlapped, out bytesWritten);
|
|
if (writeException == null && bytesWritten != this.syncWriteSize)
|
|
{
|
|
writeException = new PipeException(SR.GetString(SR.PipeWriteIncomplete));
|
|
}
|
|
}
|
|
|
|
if (writeException != null)
|
|
{
|
|
TraceEventType traceEventType = TraceEventType.Information;
|
|
if (traceExceptionsAsErrors)
|
|
{
|
|
traceEventType = TraceEventType.Error;
|
|
}
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelper(writeException, traceEventType);
|
|
}
|
|
}
|
|
|
|
enum CloseState
|
|
{
|
|
Open,
|
|
Closing,
|
|
HandleClosed,
|
|
}
|
|
|
|
enum TransferOperation
|
|
{
|
|
Write,
|
|
Read,
|
|
Undefined,
|
|
}
|
|
|
|
static class Exceptions
|
|
{
|
|
static PipeException CreateException(string resourceString, int error)
|
|
{
|
|
return new PipeException(SR.GetString(resourceString, PipeError.GetErrorString(error)), error);
|
|
}
|
|
|
|
public static PipeException CreateReadException(int error)
|
|
{
|
|
return CreateException(SR.PipeReadError, error);
|
|
}
|
|
|
|
public static PipeException CreateWriteException(int error)
|
|
{
|
|
return CreateException(SR.PipeWriteError, error);
|
|
}
|
|
|
|
// Must be called in a lock, after checking for HandleClosed.
|
|
[PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
|
|
public static unsafe PipeException GetOverlappedWriteException(PipeHandle pipe,
|
|
NativeOverlapped* nativeOverlapped, out int bytesWritten)
|
|
{
|
|
if (UnsafeNativeMethods.GetOverlappedResult(pipe.DangerousGetHandle(), nativeOverlapped, out bytesWritten, 0) == 0)
|
|
{
|
|
int error = Marshal.GetLastWin32Error();
|
|
return Exceptions.CreateWriteException(error);
|
|
}
|
|
else
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
// Must be called in a lock, after checking for HandleClosed.
|
|
[PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
|
|
public static unsafe PipeException GetOverlappedReadException(PipeHandle pipe,
|
|
NativeOverlapped* nativeOverlapped, out int bytesRead)
|
|
{
|
|
if (UnsafeNativeMethods.GetOverlappedResult(pipe.DangerousGetHandle(), nativeOverlapped, out bytesRead, 0) == 0)
|
|
{
|
|
int error = Marshal.GetLastWin32Error();
|
|
if (error == UnsafeNativeMethods.ERROR_MORE_DATA)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
else
|
|
{
|
|
return Exceptions.CreateReadException(error);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
class PipeConnectionInitiator : IConnectionInitiator
|
|
{
|
|
int bufferSize;
|
|
IPipeTransportFactorySettings pipeSettings;
|
|
|
|
public PipeConnectionInitiator(int bufferSize, IPipeTransportFactorySettings pipeSettings)
|
|
{
|
|
this.bufferSize = bufferSize;
|
|
this.pipeSettings = pipeSettings;
|
|
}
|
|
|
|
Exception CreateConnectFailedException(Uri remoteUri, PipeException innerException)
|
|
{
|
|
return new CommunicationException(
|
|
SR.GetString(SR.PipeConnectFailed, remoteUri.AbsoluteUri), innerException);
|
|
}
|
|
|
|
public IConnection Connect(Uri remoteUri, TimeSpan timeout)
|
|
{
|
|
string resolvedAddress;
|
|
BackoffTimeoutHelper backoffHelper;
|
|
TimeoutHelper timeoutHelper = new TimeoutHelper(timeout);
|
|
this.PrepareConnect(remoteUri, timeoutHelper.RemainingTime(), out resolvedAddress, out backoffHelper);
|
|
|
|
IConnection connection = null;
|
|
while (connection == null)
|
|
{
|
|
connection = this.TryConnect(remoteUri, resolvedAddress, backoffHelper);
|
|
if (connection == null)
|
|
{
|
|
backoffHelper.WaitAndBackoff();
|
|
|
|
if (DiagnosticUtility.ShouldTraceInformation)
|
|
{
|
|
TraceUtility.TraceEvent(
|
|
TraceEventType.Information,
|
|
TraceCode.FailedPipeConnect,
|
|
SR.GetString(
|
|
SR.TraceCodeFailedPipeConnect,
|
|
timeoutHelper.RemainingTime(),
|
|
remoteUri));
|
|
}
|
|
}
|
|
}
|
|
return connection;
|
|
}
|
|
|
|
internal static string GetPipeName(Uri uri, IPipeTransportFactorySettings transportFactorySettings)
|
|
{
|
|
AppContainerInfo appInfo = GetAppContainerInfo(transportFactorySettings);
|
|
|
|
// for wildcard hostName support, we first try and connect to the StrongWildcard,
|
|
// then the Exact HostName, and lastly the WeakWildcard
|
|
string[] hostChoices = new string[] { "+", uri.Host, "*" };
|
|
bool[] globalChoices = new bool[] { true, false };
|
|
string matchPath = String.Empty;
|
|
string matchPipeName = null;
|
|
|
|
for (int i = 0; i < hostChoices.Length; i++)
|
|
{
|
|
for (int iGlobal = 0; iGlobal < globalChoices.Length; iGlobal++)
|
|
{
|
|
|
|
if (appInfo != null && globalChoices[iGlobal])
|
|
{
|
|
// Don't look at shared memory to acces pipes
|
|
// that are created in the local NamedObjectPath
|
|
continue;
|
|
}
|
|
|
|
// walk up the path hierarchy, looking for match
|
|
string path = PipeUri.GetPath(uri);
|
|
|
|
while (path.Length > 0)
|
|
{
|
|
|
|
string sharedMemoryName = PipeUri.BuildSharedMemoryName(hostChoices[i], path, globalChoices[iGlobal], appInfo);
|
|
try
|
|
{
|
|
PipeSharedMemory sharedMemory = PipeSharedMemory.Open(sharedMemoryName, uri);
|
|
if (sharedMemory != null)
|
|
{
|
|
try
|
|
{
|
|
string pipeName = sharedMemory.GetPipeName(appInfo);
|
|
if (pipeName != null)
|
|
{
|
|
// Found a matching pipe name.
|
|
// If the best match app setting is enabled, save the match if it is the best so far and continue.
|
|
// Otherwise, just return the first match we find.
|
|
if (ServiceModelAppSettings.UseBestMatchNamedPipeUri)
|
|
{
|
|
if (path.Length > matchPath.Length)
|
|
{
|
|
matchPath = path;
|
|
matchPipeName = pipeName;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return pipeName;
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
sharedMemory.Dispose();
|
|
}
|
|
}
|
|
}
|
|
catch (AddressAccessDeniedException exception)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new EndpointNotFoundException(SR.GetString(
|
|
SR.EndpointNotFound, uri.AbsoluteUri), exception));
|
|
}
|
|
|
|
path = PipeUri.GetParentPath(path);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(matchPipeName))
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
|
new EndpointNotFoundException(SR.GetString(SR.EndpointNotFound, uri.AbsoluteUri),
|
|
new PipeException(SR.GetString(SR.PipeEndpointNotFound, uri.AbsoluteUri))));
|
|
}
|
|
|
|
return matchPipeName;
|
|
}
|
|
|
|
public IAsyncResult BeginConnect(Uri uri, TimeSpan timeout, AsyncCallback callback, object state)
|
|
{
|
|
return new ConnectAsyncResult(this, uri, timeout, callback, state);
|
|
}
|
|
|
|
public IConnection EndConnect(IAsyncResult result)
|
|
{
|
|
return ConnectAsyncResult.End(result);
|
|
}
|
|
|
|
void PrepareConnect(Uri remoteUri, TimeSpan timeout, out string resolvedAddress, out BackoffTimeoutHelper backoffHelper)
|
|
{
|
|
PipeUri.Validate(remoteUri);
|
|
if (DiagnosticUtility.ShouldTraceInformation)
|
|
{
|
|
TraceUtility.TraceEvent(System.Diagnostics.TraceEventType.Information, TraceCode.InitiatingNamedPipeConnection,
|
|
SR.GetString(SR.TraceCodeInitiatingNamedPipeConnection),
|
|
new StringTraceRecord("Uri", remoteUri.ToString()), this, null);
|
|
}
|
|
resolvedAddress = GetPipeName(remoteUri, this.pipeSettings);
|
|
const int backoffBufferMilliseconds = 150;
|
|
TimeSpan backoffTimeout;
|
|
if (timeout >= TimeSpan.FromMilliseconds(backoffBufferMilliseconds * 2))
|
|
{
|
|
backoffTimeout = TimeoutHelper.Add(timeout, TimeSpan.Zero - TimeSpan.FromMilliseconds(backoffBufferMilliseconds));
|
|
}
|
|
else
|
|
{
|
|
backoffTimeout = Ticks.ToTimeSpan((Ticks.FromMilliseconds(backoffBufferMilliseconds) / 2) + 1);
|
|
}
|
|
|
|
backoffHelper = new BackoffTimeoutHelper(backoffTimeout, TimeSpan.FromMinutes(5));
|
|
}
|
|
|
|
[ResourceConsumption(ResourceScope.Machine)]
|
|
IConnection TryConnect(Uri remoteUri, string resolvedAddress, BackoffTimeoutHelper backoffHelper)
|
|
{
|
|
const int access = UnsafeNativeMethods.GENERIC_READ | UnsafeNativeMethods.GENERIC_WRITE;
|
|
bool lastAttempt = backoffHelper.IsExpired();
|
|
|
|
int flags = UnsafeNativeMethods.FILE_FLAG_OVERLAPPED;
|
|
|
|
// By default Windows named pipe connection is created with impersonation, but we want
|
|
// to create it with anonymous and let WCF take care of impersonation/identification.
|
|
flags |= UnsafeNativeMethods.SECURITY_QOS_PRESENT | UnsafeNativeMethods.SECURITY_ANONYMOUS;
|
|
|
|
PipeHandle pipeHandle = UnsafeNativeMethods.CreateFile(resolvedAddress, access, 0, IntPtr.Zero,
|
|
UnsafeNativeMethods.OPEN_EXISTING, flags, IntPtr.Zero);
|
|
int error = Marshal.GetLastWin32Error();
|
|
if (pipeHandle.IsInvalid)
|
|
{
|
|
pipeHandle.SetHandleAsInvalid();
|
|
}
|
|
else
|
|
{
|
|
int mode = UnsafeNativeMethods.PIPE_READMODE_MESSAGE;
|
|
if (UnsafeNativeMethods.SetNamedPipeHandleState(pipeHandle, ref mode, IntPtr.Zero, IntPtr.Zero) == 0)
|
|
{
|
|
error = Marshal.GetLastWin32Error();
|
|
pipeHandle.Close();
|
|
PipeException innerException = new PipeException(SR.GetString(SR.PipeModeChangeFailed,
|
|
PipeError.GetErrorString(error)), error);
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
|
CreateConnectFailedException(remoteUri, innerException));
|
|
}
|
|
return new PipeConnection(pipeHandle, bufferSize, false, true);
|
|
}
|
|
|
|
if (error == UnsafeNativeMethods.ERROR_FILE_NOT_FOUND || error == UnsafeNativeMethods.ERROR_PIPE_BUSY)
|
|
{
|
|
if (lastAttempt)
|
|
{
|
|
Exception innerException = new PipeException(SR.GetString(SR.PipeConnectAddressFailed,
|
|
resolvedAddress, PipeError.GetErrorString(error)), error);
|
|
|
|
TimeoutException timeoutException;
|
|
string endpoint = remoteUri.AbsoluteUri;
|
|
|
|
if (error == UnsafeNativeMethods.ERROR_PIPE_BUSY)
|
|
{
|
|
timeoutException = new TimeoutException(SR.GetString(SR.PipeConnectTimedOutServerTooBusy,
|
|
endpoint, backoffHelper.OriginalTimeout), innerException);
|
|
}
|
|
else
|
|
{
|
|
timeoutException = new TimeoutException(SR.GetString(SR.PipeConnectTimedOut,
|
|
endpoint, backoffHelper.OriginalTimeout), innerException);
|
|
}
|
|
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(timeoutException);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
else
|
|
{
|
|
PipeException innerException = new PipeException(SR.GetString(SR.PipeConnectAddressFailed,
|
|
resolvedAddress, PipeError.GetErrorString(error)), error);
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
|
CreateConnectFailedException(remoteUri, innerException));
|
|
}
|
|
}
|
|
|
|
static AppContainerInfo GetAppContainerInfo(IPipeTransportFactorySettings transportFactorySettings)
|
|
{
|
|
if (AppContainerInfo.IsAppContainerSupported &&
|
|
transportFactorySettings != null &&
|
|
transportFactorySettings.PipeSettings != null)
|
|
{
|
|
ApplicationContainerSettings appSettings = transportFactorySettings.PipeSettings.ApplicationContainerSettings;
|
|
if (appSettings != null && appSettings.TargetingAppContainer)
|
|
{
|
|
return AppContainerInfo.CreateAppContainerInfo(appSettings.PackageFullName, appSettings.SessionId);
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
class ConnectAsyncResult : AsyncResult
|
|
{
|
|
PipeConnectionInitiator parent;
|
|
Uri remoteUri;
|
|
string resolvedAddress;
|
|
BackoffTimeoutHelper backoffHelper;
|
|
TimeoutHelper timeoutHelper;
|
|
IConnection connection;
|
|
static Action<object> waitCompleteCallback;
|
|
|
|
public ConnectAsyncResult(PipeConnectionInitiator parent, Uri remoteUri, TimeSpan timeout,
|
|
AsyncCallback callback, object state)
|
|
: base(callback, state)
|
|
{
|
|
this.parent = parent;
|
|
this.remoteUri = remoteUri;
|
|
this.timeoutHelper = new TimeoutHelper(timeout);
|
|
parent.PrepareConnect(remoteUri, this.timeoutHelper.RemainingTime(), out this.resolvedAddress, out this.backoffHelper);
|
|
|
|
if (this.ConnectAndWait())
|
|
{
|
|
this.Complete(true);
|
|
}
|
|
}
|
|
|
|
bool ConnectAndWait()
|
|
{
|
|
this.connection = this.parent.TryConnect(this.remoteUri, this.resolvedAddress, this.backoffHelper);
|
|
bool completed = (this.connection != null);
|
|
if (!completed)
|
|
{
|
|
if (waitCompleteCallback == null)
|
|
{
|
|
waitCompleteCallback = new Action<object>(OnWaitComplete);
|
|
}
|
|
this.backoffHelper.WaitAndBackoff(waitCompleteCallback, this);
|
|
}
|
|
return completed;
|
|
}
|
|
|
|
public static IConnection End(IAsyncResult result)
|
|
{
|
|
ConnectAsyncResult thisPtr = AsyncResult.End<ConnectAsyncResult>(result);
|
|
return thisPtr.connection;
|
|
}
|
|
|
|
static void OnWaitComplete(object state)
|
|
{
|
|
Exception exception = null;
|
|
ConnectAsyncResult thisPtr = (ConnectAsyncResult)state;
|
|
|
|
bool completeSelf = true;
|
|
try
|
|
{
|
|
if (DiagnosticUtility.ShouldTraceInformation)
|
|
{
|
|
TraceUtility.TraceEvent(
|
|
TraceEventType.Information,
|
|
TraceCode.FailedPipeConnect,
|
|
SR.GetString(
|
|
SR.TraceCodeFailedPipeConnect,
|
|
thisPtr.timeoutHelper.RemainingTime(),
|
|
thisPtr.remoteUri));
|
|
}
|
|
|
|
completeSelf = thisPtr.ConnectAndWait();
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e))
|
|
{
|
|
throw;
|
|
}
|
|
exception = e;
|
|
}
|
|
|
|
if (completeSelf)
|
|
{
|
|
thisPtr.Complete(false, exception);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
class PipeConnectionListener : IConnectionListener
|
|
{
|
|
Uri pipeUri;
|
|
int bufferSize;
|
|
HostNameComparisonMode hostNameComparisonMode;
|
|
bool isDisposed;
|
|
bool isListening;
|
|
List<PendingAccept> pendingAccepts;
|
|
bool anyPipesCreated;
|
|
PipeSharedMemory sharedMemory;
|
|
List<SecurityIdentifier> allowedSids;
|
|
bool useCompletionPort;
|
|
int maxInstances;
|
|
|
|
public PipeConnectionListener(Uri pipeUri, HostNameComparisonMode hostNameComparisonMode, int bufferSize,
|
|
List<SecurityIdentifier> allowedSids, bool useCompletionPort, int maxConnections)
|
|
{
|
|
PipeUri.Validate(pipeUri);
|
|
this.pipeUri = pipeUri;
|
|
this.hostNameComparisonMode = hostNameComparisonMode;
|
|
this.allowedSids = allowedSids;
|
|
this.bufferSize = bufferSize;
|
|
pendingAccepts = new List<PendingAccept>();
|
|
this.useCompletionPort = useCompletionPort;
|
|
this.maxInstances = Math.Min(maxConnections, UnsafeNativeMethods.PIPE_UNLIMITED_INSTANCES);
|
|
}
|
|
|
|
object ThisLock
|
|
{
|
|
get { return this; }
|
|
}
|
|
|
|
public string PipeName { get { return sharedMemory.PipeName; } }
|
|
|
|
public IAsyncResult BeginAccept(AsyncCallback callback, object state)
|
|
{
|
|
lock (ThisLock)
|
|
{
|
|
if (isDisposed)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ObjectDisposedException("", SR.GetString(SR.PipeListenerDisposed)));
|
|
}
|
|
|
|
if (!isListening)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.PipeListenerNotListening)));
|
|
}
|
|
|
|
PipeHandle pipeHandle = CreatePipe();
|
|
PendingAccept pendingAccept = new PendingAccept(this, pipeHandle, useCompletionPort, callback, state);
|
|
if (!pendingAccept.CompletedSynchronously)
|
|
{
|
|
this.pendingAccepts.Add(pendingAccept);
|
|
}
|
|
return pendingAccept;
|
|
}
|
|
}
|
|
|
|
public IConnection EndAccept(IAsyncResult result)
|
|
{
|
|
PendingAccept pendingAccept = result as PendingAccept;
|
|
if (pendingAccept == null)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("result", SR.GetString(SR.InvalidAsyncResult));
|
|
}
|
|
|
|
PipeHandle acceptedPipe = pendingAccept.End();
|
|
|
|
if (acceptedPipe == null)
|
|
{
|
|
return null;
|
|
}
|
|
else
|
|
{
|
|
return new PipeConnection(acceptedPipe, bufferSize,
|
|
pendingAccept.IsBoundToCompletionPort, pendingAccept.IsBoundToCompletionPort);
|
|
}
|
|
}
|
|
|
|
[PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
|
|
[ResourceConsumption(ResourceScope.Machine)]
|
|
unsafe PipeHandle CreatePipe()
|
|
{
|
|
int openMode = UnsafeNativeMethods.PIPE_ACCESS_DUPLEX | UnsafeNativeMethods.FILE_FLAG_OVERLAPPED;
|
|
if (!anyPipesCreated)
|
|
{
|
|
openMode |= UnsafeNativeMethods.FILE_FLAG_FIRST_PIPE_INSTANCE;
|
|
}
|
|
|
|
byte[] binarySecurityDescriptor;
|
|
|
|
try
|
|
{
|
|
binarySecurityDescriptor = SecurityDescriptorHelper.FromSecurityIdentifiers(allowedSids, UnsafeNativeMethods.GENERIC_READ | UnsafeNativeMethods.GENERIC_WRITE);
|
|
}
|
|
catch (Win32Exception e)
|
|
{
|
|
// While Win32exceptions are not expected, if they do occur we need to obey the pipe/communication exception model.
|
|
Exception innerException = new PipeException(e.Message, e);
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(innerException.Message, innerException));
|
|
}
|
|
|
|
PipeHandle pipeHandle;
|
|
int error;
|
|
string pipeName = null;
|
|
fixed (byte* pinnedSecurityDescriptor = binarySecurityDescriptor)
|
|
{
|
|
UnsafeNativeMethods.SECURITY_ATTRIBUTES securityAttributes = new UnsafeNativeMethods.SECURITY_ATTRIBUTES();
|
|
securityAttributes.lpSecurityDescriptor = (IntPtr)pinnedSecurityDescriptor;
|
|
|
|
pipeName = this.sharedMemory.PipeName;
|
|
pipeHandle = UnsafeNativeMethods.CreateNamedPipe(
|
|
pipeName,
|
|
openMode,
|
|
UnsafeNativeMethods.PIPE_TYPE_MESSAGE | UnsafeNativeMethods.PIPE_READMODE_MESSAGE,
|
|
maxInstances, bufferSize, bufferSize, 0, securityAttributes);
|
|
error = Marshal.GetLastWin32Error();
|
|
}
|
|
|
|
if (pipeHandle.IsInvalid)
|
|
{
|
|
pipeHandle.SetHandleAsInvalid();
|
|
|
|
Exception innerException = new PipeException(SR.GetString(SR.PipeListenFailed,
|
|
pipeUri.AbsoluteUri, PipeError.GetErrorString(error)), error);
|
|
|
|
if (error == UnsafeNativeMethods.ERROR_ACCESS_DENIED)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new AddressAccessDeniedException(innerException.Message, innerException));
|
|
}
|
|
else if (error == UnsafeNativeMethods.ERROR_ALREADY_EXISTS)
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new AddressAlreadyInUseException(innerException.Message, innerException));
|
|
}
|
|
else
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(innerException.Message, innerException));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (TD.NamedPipeCreatedIsEnabled())
|
|
{
|
|
TD.NamedPipeCreated(pipeName);
|
|
}
|
|
}
|
|
|
|
bool closePipe = true;
|
|
try
|
|
{
|
|
if (useCompletionPort)
|
|
{
|
|
ThreadPool.BindHandle(pipeHandle);
|
|
}
|
|
anyPipesCreated = true;
|
|
closePipe = false;
|
|
return pipeHandle;
|
|
}
|
|
finally
|
|
{
|
|
if (closePipe)
|
|
{
|
|
pipeHandle.Close();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
lock (ThisLock)
|
|
{
|
|
if (!isDisposed)
|
|
{
|
|
if (sharedMemory != null)
|
|
{
|
|
sharedMemory.Dispose();
|
|
}
|
|
for (int i = 0; i < pendingAccepts.Count; i++)
|
|
{
|
|
pendingAccepts[i].Abort();
|
|
}
|
|
isDisposed = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Listen()
|
|
{
|
|
lock (ThisLock)
|
|
{
|
|
if (!isListening)
|
|
{
|
|
string sharedMemoryName = PipeUri.BuildSharedMemoryName(pipeUri, hostNameComparisonMode, true);
|
|
if (!PipeSharedMemory.TryCreate(allowedSids, pipeUri, sharedMemoryName, out this.sharedMemory))
|
|
{
|
|
PipeSharedMemory tempSharedMemory = null;
|
|
|
|
// first see if we're in RANU by creating a unique Uri in the global namespace
|
|
Uri tempUri = new Uri(pipeUri, Guid.NewGuid().ToString());
|
|
string tempSharedMemoryName = PipeUri.BuildSharedMemoryName(tempUri, hostNameComparisonMode, true);
|
|
if (PipeSharedMemory.TryCreate(allowedSids, tempUri, tempSharedMemoryName, out tempSharedMemory))
|
|
{
|
|
// we're not RANU, throw PipeNameInUse
|
|
tempSharedMemory.Dispose();
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(
|
|
PipeSharedMemory.CreatePipeNameInUseException(UnsafeNativeMethods.ERROR_ACCESS_DENIED, pipeUri));
|
|
}
|
|
else
|
|
{
|
|
// try the session namespace since we're RANU
|
|
sharedMemoryName = PipeUri.BuildSharedMemoryName(pipeUri, hostNameComparisonMode, false);
|
|
this.sharedMemory = PipeSharedMemory.Create(allowedSids, pipeUri, sharedMemoryName);
|
|
}
|
|
}
|
|
|
|
isListening = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
void RemovePendingAccept(PendingAccept pendingAccept)
|
|
{
|
|
lock (ThisLock)
|
|
{
|
|
Fx.Assert(this.pendingAccepts.Contains(pendingAccept), "An unknown PendingAccept is removing itself.");
|
|
this.pendingAccepts.Remove(pendingAccept);
|
|
}
|
|
}
|
|
|
|
class PendingAccept : AsyncResult
|
|
{
|
|
PipeHandle pipeHandle;
|
|
PipeHandle result;
|
|
OverlappedIOCompleteCallback onAcceptComplete;
|
|
static Action<object> onStartAccept;
|
|
OverlappedContext overlapped;
|
|
bool isBoundToCompletionPort;
|
|
PipeConnectionListener listener;
|
|
EventTraceActivity eventTraceActivity;
|
|
|
|
[PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
|
|
public unsafe PendingAccept(PipeConnectionListener listener, PipeHandle pipeHandle, bool isBoundToCompletionPort,
|
|
AsyncCallback callback, object state)
|
|
: base(callback, state)
|
|
{
|
|
this.pipeHandle = pipeHandle;
|
|
this.result = pipeHandle;
|
|
this.listener = listener;
|
|
onAcceptComplete = new OverlappedIOCompleteCallback(OnAcceptComplete);
|
|
overlapped = new OverlappedContext();
|
|
this.isBoundToCompletionPort = isBoundToCompletionPort;
|
|
|
|
if (TD.PipeConnectionAcceptStartIsEnabled())
|
|
{
|
|
this.eventTraceActivity = new EventTraceActivity();
|
|
TD.PipeConnectionAcceptStart(this.eventTraceActivity, this.listener.pipeUri != null ? this.listener.pipeUri.ToString() : string.Empty);
|
|
}
|
|
|
|
if (!Thread.CurrentThread.IsThreadPoolThread)
|
|
{
|
|
if (onStartAccept == null)
|
|
{
|
|
onStartAccept = new Action<object>(OnStartAccept);
|
|
}
|
|
ActionItem.Schedule(onStartAccept, this);
|
|
}
|
|
else
|
|
{
|
|
StartAccept(true);
|
|
}
|
|
}
|
|
|
|
public bool IsBoundToCompletionPort
|
|
{
|
|
get { return this.isBoundToCompletionPort; }
|
|
}
|
|
|
|
static void OnStartAccept(object state)
|
|
{
|
|
PendingAccept pendingAccept = (PendingAccept)state;
|
|
pendingAccept.StartAccept(false);
|
|
}
|
|
|
|
Exception CreatePipeAcceptFailedException(int errorCode)
|
|
{
|
|
Exception innerException = new PipeException(SR.GetString(SR.PipeAcceptFailed,
|
|
PipeError.GetErrorString(errorCode)), errorCode);
|
|
return new CommunicationException(innerException.Message, innerException);
|
|
}
|
|
|
|
[PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
|
|
unsafe void StartAccept(bool synchronous)
|
|
{
|
|
Exception completionException = null;
|
|
bool completeSelf = false;
|
|
try
|
|
{
|
|
try
|
|
{
|
|
this.overlapped.StartAsyncOperation(null, onAcceptComplete, this.isBoundToCompletionPort);
|
|
while (true)
|
|
{
|
|
if (UnsafeNativeMethods.ConnectNamedPipe(pipeHandle, overlapped.NativeOverlapped) == 0)
|
|
{
|
|
int error = Marshal.GetLastWin32Error();
|
|
switch (error)
|
|
{
|
|
case UnsafeNativeMethods.ERROR_NO_DATA:
|
|
if (UnsafeNativeMethods.DisconnectNamedPipe(pipeHandle) != 0)
|
|
{
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
completeSelf = true;
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreatePipeAcceptFailedException(error));
|
|
}
|
|
case UnsafeNativeMethods.ERROR_PIPE_CONNECTED:
|
|
completeSelf = true;
|
|
break;
|
|
case UnsafeNativeMethods.ERROR_IO_PENDING:
|
|
break;
|
|
default:
|
|
completeSelf = true;
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreatePipeAcceptFailedException(error));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
completeSelf = true;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
catch (ObjectDisposedException exception)
|
|
{
|
|
// A ---- with Abort can cause PipeHandle to throw this.
|
|
Fx.Assert(this.result == null, "Got an ObjectDisposedException but not an Abort!");
|
|
DiagnosticUtility.TraceHandledException(exception, TraceEventType.Information);
|
|
completeSelf = true;
|
|
}
|
|
finally
|
|
{
|
|
if (completeSelf)
|
|
{
|
|
this.overlapped.CancelAsyncOperation();
|
|
this.overlapped.Free();
|
|
}
|
|
}
|
|
}
|
|
#pragma warning suppress 56500 // Microsoft, transferring exception to another thread
|
|
catch (Exception e)
|
|
{
|
|
if (Fx.IsFatal(e))
|
|
{
|
|
throw;
|
|
}
|
|
|
|
completeSelf = true;
|
|
completionException = e;
|
|
}
|
|
if (completeSelf)
|
|
{
|
|
if (!synchronous)
|
|
{
|
|
this.listener.RemovePendingAccept(this);
|
|
}
|
|
base.Complete(synchronous, completionException);
|
|
}
|
|
}
|
|
|
|
// Must be called in PipeConnectionListener's lock.
|
|
public void Abort()
|
|
{
|
|
this.result = null; // we need to return null after an abort
|
|
pipeHandle.Close();
|
|
}
|
|
|
|
public PipeHandle End()
|
|
{
|
|
AsyncResult.End<PendingAccept>(this);
|
|
return this.result;
|
|
}
|
|
|
|
[PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
|
|
unsafe void OnAcceptComplete(bool haveResult, int error, int numBytes)
|
|
{
|
|
this.listener.RemovePendingAccept(this);
|
|
|
|
if (!haveResult)
|
|
{
|
|
// No ---- with Abort here since Abort can't be called once RemovePendingAccept happens.
|
|
if (this.result != null && UnsafeNativeMethods.GetOverlappedResult(this.pipeHandle,
|
|
this.overlapped.NativeOverlapped, out numBytes, 0) == 0)
|
|
{
|
|
error = Marshal.GetLastWin32Error();
|
|
}
|
|
else
|
|
{
|
|
error = 0;
|
|
}
|
|
}
|
|
|
|
this.overlapped.Free();
|
|
|
|
if (TD.PipeConnectionAcceptStopIsEnabled())
|
|
{
|
|
TD.PipeConnectionAcceptStop(this.eventTraceActivity);
|
|
}
|
|
|
|
if (error != 0)
|
|
{
|
|
this.pipeHandle.Close();
|
|
base.Complete(false, CreatePipeAcceptFailedException(error));
|
|
}
|
|
else
|
|
{
|
|
base.Complete(false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static class SecurityDescriptorHelper
|
|
{
|
|
static byte[] worldCreatorOwnerWithReadAndWriteDescriptorDenyNetwork;
|
|
static byte[] worldCreatorOwnerWithReadDescriptorDenyNetwork;
|
|
|
|
static SecurityDescriptorHelper()
|
|
{
|
|
worldCreatorOwnerWithReadAndWriteDescriptorDenyNetwork = FromSecurityIdentifiersFull(null, UnsafeNativeMethods.GENERIC_READ | UnsafeNativeMethods.GENERIC_WRITE);
|
|
worldCreatorOwnerWithReadDescriptorDenyNetwork = FromSecurityIdentifiersFull(null, UnsafeNativeMethods.GENERIC_READ);
|
|
}
|
|
|
|
internal static byte[] FromSecurityIdentifiers(List<SecurityIdentifier> allowedSids, int accessRights)
|
|
{
|
|
if (allowedSids == null)
|
|
{
|
|
if (accessRights == (UnsafeNativeMethods.GENERIC_READ | UnsafeNativeMethods.GENERIC_WRITE))
|
|
{
|
|
return worldCreatorOwnerWithReadAndWriteDescriptorDenyNetwork;
|
|
}
|
|
|
|
if (accessRights == UnsafeNativeMethods.GENERIC_READ)
|
|
{
|
|
return worldCreatorOwnerWithReadDescriptorDenyNetwork;
|
|
}
|
|
}
|
|
|
|
return FromSecurityIdentifiersFull(allowedSids, accessRights);
|
|
}
|
|
|
|
static byte[] FromSecurityIdentifiersFull(List<SecurityIdentifier> allowedSids, int accessRights)
|
|
{
|
|
int capacity = allowedSids == null ? 3 : 2 + allowedSids.Count;
|
|
DiscretionaryAcl dacl = new DiscretionaryAcl(false, false, capacity);
|
|
|
|
// add deny ACE first so that we don't get short circuited
|
|
dacl.AddAccess(AccessControlType.Deny, new SecurityIdentifier(WellKnownSidType.NetworkSid, null),
|
|
UnsafeNativeMethods.GENERIC_ALL, InheritanceFlags.None, PropagationFlags.None);
|
|
|
|
// clients get different rights, since they shouldn't be able to listen
|
|
int clientAccessRights = GenerateClientAccessRights(accessRights);
|
|
|
|
if (allowedSids == null)
|
|
{
|
|
dacl.AddAccess(AccessControlType.Allow, new SecurityIdentifier(WellKnownSidType.WorldSid, null),
|
|
clientAccessRights, InheritanceFlags.None, PropagationFlags.None);
|
|
}
|
|
else
|
|
{
|
|
for (int i = 0; i < allowedSids.Count; i++)
|
|
{
|
|
SecurityIdentifier allowedSid = allowedSids[i];
|
|
dacl.AddAccess(AccessControlType.Allow, allowedSid,
|
|
clientAccessRights, InheritanceFlags.None, PropagationFlags.None);
|
|
}
|
|
}
|
|
|
|
dacl.AddAccess(AccessControlType.Allow, GetProcessLogonSid(), accessRights, InheritanceFlags.None, PropagationFlags.None);
|
|
|
|
|
|
if (AppContainerInfo.IsRunningInAppContainer)
|
|
{
|
|
// NamedPipeBinding requires dacl with current AppContainer SID
|
|
// to setup multiple NamedPipes in the BeginAccept loop.
|
|
dacl.AddAccess(
|
|
AccessControlType.Allow,
|
|
AppContainerInfo.GetCurrentAppContainerSid(),
|
|
accessRights,
|
|
InheritanceFlags.None,
|
|
PropagationFlags.None);
|
|
}
|
|
|
|
CommonSecurityDescriptor securityDescriptor =
|
|
new CommonSecurityDescriptor(false, false, ControlFlags.None, null, null, null, dacl);
|
|
byte[] binarySecurityDescriptor = new byte[securityDescriptor.BinaryLength];
|
|
securityDescriptor.GetBinaryForm(binarySecurityDescriptor, 0);
|
|
return binarySecurityDescriptor;
|
|
}
|
|
|
|
// Security: We cannot grant rights for FILE_CREATE_PIPE_INSTANCE to clients, otherwise other apps can intercept server side pipes.
|
|
// FILE_CREATE_PIPE_INSTANCE is granted in 2 ways, via GENERIC_WRITE or directly specified. Remove both.
|
|
static int GenerateClientAccessRights(int accessRights)
|
|
{
|
|
int everyoneAccessRights = accessRights;
|
|
|
|
if ((everyoneAccessRights & UnsafeNativeMethods.GENERIC_WRITE) != 0)
|
|
{
|
|
everyoneAccessRights &= ~UnsafeNativeMethods.GENERIC_WRITE;
|
|
|
|
// Since GENERIC_WRITE grants the permissions to write to a file, we need to add it back.
|
|
const int clientWriteAccess = UnsafeNativeMethods.FILE_WRITE_ATTRIBUTES | UnsafeNativeMethods.FILE_WRITE_DATA | UnsafeNativeMethods.FILE_WRITE_EA;
|
|
everyoneAccessRights |= clientWriteAccess;
|
|
}
|
|
|
|
// Future proofing: FILE_CREATE_PIPE_INSTANCE isn't used currently but we need to ensure it is not granted.
|
|
everyoneAccessRights &= ~UnsafeNativeMethods.FILE_CREATE_PIPE_INSTANCE;
|
|
|
|
return everyoneAccessRights;
|
|
}
|
|
|
|
// The logon sid is generated on process start up so it is unique to this process.
|
|
static SecurityIdentifier GetProcessLogonSid()
|
|
{
|
|
int pid = Process.GetCurrentProcess().Id;
|
|
return System.ServiceModel.Activation.Utility.GetLogonSidForPid(pid);
|
|
}
|
|
}
|
|
|
|
unsafe class PipeSharedMemory : IDisposable
|
|
{
|
|
internal const string PipePrefix = @"\\.\pipe\";
|
|
internal const string PipeLocalPrefix = @"\\.\pipe\Local\";
|
|
SafeFileMappingHandle fileMapping;
|
|
string pipeName;
|
|
string pipeNameGuidPart;
|
|
Uri pipeUri;
|
|
|
|
PipeSharedMemory(SafeFileMappingHandle fileMapping, Uri pipeUri)
|
|
: this(fileMapping, pipeUri, null)
|
|
{
|
|
}
|
|
|
|
PipeSharedMemory(SafeFileMappingHandle fileMapping, Uri pipeUri, string pipeName)
|
|
{
|
|
this.pipeName = pipeName;
|
|
this.fileMapping = fileMapping;
|
|
this.pipeUri = pipeUri;
|
|
}
|
|
|
|
public static PipeSharedMemory Create(List<SecurityIdentifier> allowedSids, Uri pipeUri, string sharedMemoryName)
|
|
{
|
|
PipeSharedMemory result;
|
|
if (TryCreate(allowedSids, pipeUri, sharedMemoryName, out result))
|
|
{
|
|
return result;
|
|
}
|
|
else
|
|
{
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreatePipeNameInUseException(UnsafeNativeMethods.ERROR_ACCESS_DENIED, pipeUri));
|
|
}
|
|
}
|
|
|
|
[PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
|
|
public unsafe static bool TryCreate(List<SecurityIdentifier> allowedSids, Uri pipeUri, string sharedMemoryName, out PipeSharedMemory result)
|
|
{
|
|
Guid pipeGuid = Guid.NewGuid();
|
|
string pipeName = BuildPipeName(pipeGuid.ToString());
|
|
byte[] binarySecurityDescriptor;
|
|
try
|
|
{
|
|
binarySecurityDescriptor = SecurityDescriptorHelper.FromSecurityIdentifiers(allowedSids, UnsafeNativeMethods.GENERIC_READ);
|
|
}
|
|
catch (Win32Exception e)
|
|
{
|
|
// While Win32exceptions are not expected, if they do occur we need to obey the pipe/communication exception model.
|
|
Exception innerException = new PipeException(e.Message, e);
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new CommunicationException(innerException.Message, innerException));
|
|
}
|
|
|
|
SafeFileMappingHandle fileMapping;
|
|
int error;
|
|
result = null;
|
|
fixed (byte* pinnedSecurityDescriptor = binarySecurityDescriptor)
|
|
{
|
|
UnsafeNativeMethods.SECURITY_ATTRIBUTES securityAttributes = new UnsafeNativeMethods.SECURITY_ATTRIBUTES();
|
|
securityAttributes.lpSecurityDescriptor = (IntPtr)pinnedSecurityDescriptor;
|
|
|
|
fileMapping = UnsafeNativeMethods.CreateFileMapping((IntPtr)(-1), securityAttributes,
|
|
UnsafeNativeMethods.PAGE_READWRITE, 0, sizeof(SharedMemoryContents), sharedMemoryName);
|
|
error = Marshal.GetLastWin32Error();
|
|
}
|
|
|
|
if (fileMapping.IsInvalid)
|
|
{
|
|
fileMapping.SetHandleAsInvalid();
|
|
if (error == UnsafeNativeMethods.ERROR_ACCESS_DENIED)
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
Exception innerException = new PipeException(SR.GetString(SR.PipeNameCantBeReserved,
|
|
pipeUri.AbsoluteUri, PipeError.GetErrorString(error)), error);
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new AddressAccessDeniedException(innerException.Message, innerException));
|
|
}
|
|
}
|
|
|
|
// now we have a valid file mapping handle
|
|
if (error == UnsafeNativeMethods.ERROR_ALREADY_EXISTS)
|
|
{
|
|
fileMapping.Close();
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreatePipeNameInUseException(error, pipeUri));
|
|
}
|
|
PipeSharedMemory pipeSharedMemory = new PipeSharedMemory(fileMapping, pipeUri, pipeName);
|
|
bool disposeSharedMemory = true;
|
|
try
|
|
{
|
|
pipeSharedMemory.InitializeContents(pipeGuid);
|
|
disposeSharedMemory = false;
|
|
result = pipeSharedMemory;
|
|
|
|
if (TD.PipeSharedMemoryCreatedIsEnabled())
|
|
{
|
|
TD.PipeSharedMemoryCreated(sharedMemoryName);
|
|
}
|
|
return true;
|
|
}
|
|
finally
|
|
{
|
|
if (disposeSharedMemory)
|
|
{
|
|
pipeSharedMemory.Dispose();
|
|
}
|
|
}
|
|
}
|
|
|
|
[ResourceConsumption(ResourceScope.Machine)]
|
|
public static PipeSharedMemory Open(string sharedMemoryName, Uri pipeUri)
|
|
{
|
|
SafeFileMappingHandle fileMapping = UnsafeNativeMethods.OpenFileMapping(
|
|
UnsafeNativeMethods.FILE_MAP_READ, false, sharedMemoryName);
|
|
if (fileMapping.IsInvalid)
|
|
{
|
|
int error = Marshal.GetLastWin32Error();
|
|
fileMapping.SetHandleAsInvalid();
|
|
if (error == UnsafeNativeMethods.ERROR_FILE_NOT_FOUND)
|
|
{
|
|
fileMapping = UnsafeNativeMethods.OpenFileMapping(
|
|
UnsafeNativeMethods.FILE_MAP_READ, false, "Global\\" + sharedMemoryName);
|
|
if (fileMapping.IsInvalid)
|
|
{
|
|
error = Marshal.GetLastWin32Error();
|
|
fileMapping.SetHandleAsInvalid();
|
|
if (error == UnsafeNativeMethods.ERROR_FILE_NOT_FOUND)
|
|
{
|
|
return null;
|
|
}
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreatePipeNameCannotBeAccessedException(error, pipeUri));
|
|
}
|
|
return new PipeSharedMemory(fileMapping, pipeUri);
|
|
}
|
|
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreatePipeNameCannotBeAccessedException(error, pipeUri));
|
|
}
|
|
return new PipeSharedMemory(fileMapping, pipeUri);
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
if (fileMapping != null)
|
|
{
|
|
fileMapping.Close();
|
|
fileMapping = null;
|
|
}
|
|
}
|
|
|
|
public string PipeName
|
|
{
|
|
[PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
|
|
get
|
|
{
|
|
if (pipeName == null)
|
|
{
|
|
SafeViewOfFileHandle view = GetView(false);
|
|
try
|
|
{
|
|
SharedMemoryContents* contents = (SharedMemoryContents*)view.DangerousGetHandle();
|
|
if (contents->isInitialized)
|
|
{
|
|
Thread.MemoryBarrier();
|
|
this.pipeNameGuidPart = contents->pipeGuid.ToString();
|
|
this.pipeName = BuildPipeName(this.pipeNameGuidPart);
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
view.Close();
|
|
}
|
|
}
|
|
return pipeName;
|
|
}
|
|
}
|
|
|
|
internal string GetPipeName(AppContainerInfo appInfo)
|
|
{
|
|
if (appInfo == null)
|
|
{
|
|
return this.PipeName;
|
|
}
|
|
else if (this.PipeName != null)
|
|
{
|
|
// Build the PipeName for a pipe inside an AppContainer as follows
|
|
// \\.\pipe\Sessions\<SessionId>\<NamedObjectPath>\<PipeGuid>
|
|
return string.Format(
|
|
CultureInfo.InvariantCulture,
|
|
@"\\.\pipe\Sessions\{0}\{1}\{2}",
|
|
appInfo.SessionId,
|
|
appInfo.NamedObjectPath,
|
|
this.pipeNameGuidPart);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
[PermissionSet(SecurityAction.Demand, Unrestricted = true), SecuritySafeCritical]
|
|
void InitializeContents(Guid pipeGuid)
|
|
{
|
|
SafeViewOfFileHandle view = GetView(true);
|
|
try
|
|
{
|
|
SharedMemoryContents* contents = (SharedMemoryContents*)view.DangerousGetHandle();
|
|
contents->pipeGuid = pipeGuid;
|
|
Thread.MemoryBarrier();
|
|
contents->isInitialized = true;
|
|
}
|
|
finally
|
|
{
|
|
view.Close();
|
|
}
|
|
}
|
|
|
|
public static Exception CreatePipeNameInUseException(int error, Uri pipeUri)
|
|
{
|
|
Exception innerException = new PipeException(SR.GetString(SR.PipeNameInUse, pipeUri.AbsoluteUri), error);
|
|
return new AddressAlreadyInUseException(innerException.Message, innerException);
|
|
}
|
|
|
|
static Exception CreatePipeNameCannotBeAccessedException(int error, Uri pipeUri)
|
|
{
|
|
Exception innerException = new PipeException(SR.GetString(SR.PipeNameCanNotBeAccessed,
|
|
PipeError.GetErrorString(error)), error);
|
|
return new AddressAccessDeniedException(SR.GetString(SR.PipeNameCanNotBeAccessed2, pipeUri.AbsoluteUri), innerException);
|
|
}
|
|
|
|
SafeViewOfFileHandle GetView(bool writable)
|
|
{
|
|
SafeViewOfFileHandle handle = UnsafeNativeMethods.MapViewOfFile(fileMapping,
|
|
writable ? UnsafeNativeMethods.FILE_MAP_WRITE : UnsafeNativeMethods.FILE_MAP_READ,
|
|
0, 0, (IntPtr)sizeof(SharedMemoryContents));
|
|
if (handle.IsInvalid)
|
|
{
|
|
int error = Marshal.GetLastWin32Error();
|
|
handle.SetHandleAsInvalid();
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(CreatePipeNameCannotBeAccessedException(error, pipeUri));
|
|
}
|
|
return handle;
|
|
}
|
|
|
|
static string BuildPipeName(string pipeGuid)
|
|
{
|
|
return (AppContainerInfo.IsRunningInAppContainer ? PipeLocalPrefix : PipePrefix) + pipeGuid;
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
struct SharedMemoryContents
|
|
{
|
|
public bool isInitialized;
|
|
public Guid pipeGuid;
|
|
}
|
|
}
|
|
|
|
static class PipeUri
|
|
{
|
|
public static string BuildSharedMemoryName(Uri uri, HostNameComparisonMode hostNameComparisonMode, bool global)
|
|
{
|
|
string path = PipeUri.GetPath(uri);
|
|
string host = null;
|
|
|
|
switch (hostNameComparisonMode)
|
|
{
|
|
case HostNameComparisonMode.StrongWildcard:
|
|
host = "+";
|
|
break;
|
|
case HostNameComparisonMode.Exact:
|
|
host = uri.Host;
|
|
break;
|
|
case HostNameComparisonMode.WeakWildcard:
|
|
host = "*";
|
|
break;
|
|
}
|
|
|
|
return PipeUri.BuildSharedMemoryName(host, path, global);
|
|
}
|
|
|
|
internal static string BuildSharedMemoryName(string hostName, string path, bool global, AppContainerInfo appContainerInfo)
|
|
{
|
|
if (appContainerInfo == null)
|
|
{
|
|
return BuildSharedMemoryName(hostName, path, global);
|
|
}
|
|
else
|
|
{
|
|
Fx.Assert(appContainerInfo.SessionId != ApplicationContainerSettingsDefaults.CurrentSession, "Session has not yet been initialized.");
|
|
Fx.Assert(!String.IsNullOrEmpty(appContainerInfo.NamedObjectPath),
|
|
"NamedObjectPath cannot be empty when creating the SharedMemoryName when running in an AppContainer.");
|
|
|
|
//We need to use a session symlink for the lowbox appcontainer.
|
|
// Session\{0}\{1}\{2}\<SharedMemoryName>
|
|
return string.Format(
|
|
CultureInfo.InvariantCulture,
|
|
@"Session\{0}\{1}\{2}",
|
|
appContainerInfo.SessionId,
|
|
appContainerInfo.NamedObjectPath,
|
|
BuildSharedMemoryName(hostName, path, global));
|
|
}
|
|
}
|
|
|
|
static string BuildSharedMemoryName(string hostName, string path, bool global)
|
|
{
|
|
StringBuilder builder = new StringBuilder();
|
|
builder.Append(Uri.UriSchemeNetPipe);
|
|
builder.Append("://");
|
|
builder.Append(hostName.ToUpperInvariant());
|
|
builder.Append(path);
|
|
string canonicalName = builder.ToString();
|
|
|
|
byte[] canonicalBytes = Encoding.UTF8.GetBytes(canonicalName);
|
|
byte[] hashedBytes;
|
|
string separator;
|
|
|
|
if (canonicalBytes.Length >= 128)
|
|
{
|
|
using (HashAlgorithm hash = GetHashAlgorithm())
|
|
{
|
|
hashedBytes = hash.ComputeHash(canonicalBytes);
|
|
}
|
|
separator = ":H";
|
|
}
|
|
else
|
|
{
|
|
hashedBytes = canonicalBytes;
|
|
separator = ":E";
|
|
}
|
|
|
|
builder = new StringBuilder();
|
|
if (global)
|
|
{
|
|
// we may need to create the shared memory in the global namespace so we work with terminal services+admin
|
|
builder.Append("Global\\");
|
|
}
|
|
else
|
|
{
|
|
builder.Append("Local\\");
|
|
}
|
|
builder.Append(Uri.UriSchemeNetPipe);
|
|
builder.Append(separator);
|
|
builder.Append(Convert.ToBase64String(hashedBytes));
|
|
return builder.ToString();
|
|
}
|
|
|
|
[SuppressMessage("Microsoft.Security.Cryptography", "CA5354:DoNotUseSHA1", Justification = "Cannot change. It will cause compatibility issue. Not used for cryptographic purposes.")]
|
|
static HashAlgorithm GetHashAlgorithm()
|
|
{
|
|
if (!LocalAppContextSwitches.UseSha1InPipeConnectionGetHashAlgorithm)
|
|
{
|
|
if (SecurityUtilsEx.RequiresFipsCompliance)
|
|
return new SHA256CryptoServiceProvider();
|
|
else
|
|
return new SHA256Managed();
|
|
}
|
|
else
|
|
{
|
|
if (SecurityUtilsEx.RequiresFipsCompliance)
|
|
return new SHA1CryptoServiceProvider();
|
|
else
|
|
return new SHA1Managed();
|
|
}
|
|
}
|
|
|
|
public static string GetPath(Uri uri)
|
|
{
|
|
string path = uri.LocalPath.ToUpperInvariant();
|
|
if (!path.EndsWith("/", StringComparison.Ordinal))
|
|
path = path + "/";
|
|
return path;
|
|
}
|
|
|
|
public static string GetParentPath(string path)
|
|
{
|
|
if (path.EndsWith("/", StringComparison.Ordinal))
|
|
path = path.Substring(0, path.Length - 1);
|
|
if (path.Length == 0)
|
|
return path;
|
|
return path.Substring(0, path.LastIndexOf('/') + 1);
|
|
}
|
|
|
|
public static void Validate(Uri uri)
|
|
{
|
|
if (uri.Scheme != Uri.UriSchemeNetPipe)
|
|
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgument("uri", SR.GetString(SR.PipeUriSchemeWrong));
|
|
}
|
|
}
|
|
|
|
static class PipeError
|
|
{
|
|
public static string GetErrorString(int error)
|
|
{
|
|
StringBuilder stringBuilder = new StringBuilder(512);
|
|
if (UnsafeNativeMethods.FormatMessage(UnsafeNativeMethods.FORMAT_MESSAGE_IGNORE_INSERTS |
|
|
UnsafeNativeMethods.FORMAT_MESSAGE_FROM_SYSTEM | UnsafeNativeMethods.FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
|
IntPtr.Zero, error, CultureInfo.CurrentCulture.LCID, stringBuilder, stringBuilder.Capacity, IntPtr.Zero) != 0)
|
|
{
|
|
stringBuilder = stringBuilder.Replace("\n", "");
|
|
stringBuilder = stringBuilder.Replace("\r", "");
|
|
return SR.GetString(
|
|
SR.PipeKnownWin32Error,
|
|
stringBuilder.ToString(),
|
|
error.ToString(CultureInfo.InvariantCulture),
|
|
Convert.ToString(error, 16));
|
|
}
|
|
else
|
|
{
|
|
return SR.GetString(
|
|
SR.PipeUnknownWin32Error,
|
|
error.ToString(CultureInfo.InvariantCulture),
|
|
Convert.ToString(error, 16));
|
|
}
|
|
}
|
|
}
|
|
}
|