Code:
/ WCF / WCF / 3.5.30729.1 / untmp / Orcas / SP / ndp / cdf / src / WCF / ServiceModel / System / ServiceModel / Channels / PipeConnection.cs / 2 / PipeConnection.cs
//------------------------------------------------------------ // Copyright (c) Microsoft Corporation. All rights reserved. //----------------------------------------------------------- namespace System.ServiceModel.Channels { using System.Collections.Generic; using System.ServiceModel; using System.Globalization; using System.Text; using System.Threading; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; using Microsoft.Win32.SafeHandles; using System.ServiceModel.Diagnostics; using System.ServiceModel.Security; using System.Security.Cryptography; using System.Security.AccessControl; using System.Security.Principal; using System.Diagnostics; using System.ComponentModel; using System.IO; using System.ServiceModel.Activation; using System.Net; using SafeCloseHandle = System.IdentityModel.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. WriteAsyncResult writeAsyncResult; OverlappedContext writeOverlapped; bool isShutdownWritten; int syncWriteSize; byte[] pendingWriteBuffer; BufferManager pendingWriteBufferManager; OverlappedIOCompleteCallback onAsyncWriteComplete; int writeBufferSize; // timeout support TimeSpan readTimeout; IOThreadTimer readTimer; static WaitCallback onReadTimeout; string timeoutErrorString; TransferOperation timeoutErrorTransferOperation; TimeSpan writeTimeout; IOThreadTimer writeTimer; static WaitCallback 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.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 { if (this.asyncReadBuffer == null) { Interlocked.CompareExchange(ref this.asyncReadBuffer, DiagnosticUtility.Utility.AllocateByteArray(AsyncReadBufferSize), null); } 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 WaitCallback(OnReadTimeout); } this.readTimer = new IOThreadTimer(onReadTimeout, this, false); } return this.readTimer; } } IOThreadTimer WriteTimer { get { if (this.writeTimer == null) { if (onWriteTimeout == null) { onWriteTimeout = new WaitCallback(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); } } public unsafe AsyncReadResult 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 AsyncReadResult.Completed; } if (autoBindToCompletionPort) { if (!isBoundToCompletionPort) { lock (writeLock) { // readLock, writeLock acquired in order to prevent deadlock EnsureBoundToCompletionPort(); } } } if (this.isReadOutstanding) { DiagnosticUtility.DebugAssert("Read I/O already pending when BeginRead called."); throw DiagnosticUtility.ExceptionUtility.ThrowHelperInternal(false); } 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 ? AsyncReadResult.Queued : AsyncReadResult.Completed; } catch (PipeException e) { throw DiagnosticUtility.ExceptionUtility.ThrowHelper(ConvertPipeException(e, TransferOperation.Read), ExceptionEventType); } } } public unsafe IAsyncResult BeginWrite(byte[] buffer, int offset, int size, bool immediate, TimeSpan timeout, AsyncCallback 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) { DiagnosticUtility.DebugAssert("Write I/O already pending when BeginWrite called."); throw DiagnosticUtility.ExceptionUtility.ThrowHelperInternal(false); } WriteAsyncResult result = new WriteAsyncResult(callback, state, size); try { this.writeTimeout = timeout; this.WriteTimer.Set(timeoutHelper.RemainingTime()); this.writeAsyncResult = result; 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.writeAsyncResult = null; 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); } result.Complete(true); } else { EnterWritingState(); } return result; } catch (PipeException e) { throw DiagnosticUtility.ExceptionUtility.ThrowHelper(ConvertPipeException(e, TransferOperation.Write), ExceptionEventType); } } } public void Close(TimeSpan timeout) { 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(), false)) { 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; } if (DiagnosticUtility.ShouldTraceInformation) { DiagnosticUtility.ExceptionUtility.TraceHandledException(e, TraceEventType.Information); } } catch (CommunicationException e) { if (DiagnosticUtility.ShouldTraceInformation) { DiagnosticUtility.ExceptionUtility.TraceHandledException(e, TraceEventType.Information); } } catch (TimeoutException e) { if (DiagnosticUtility.ShouldTraceInformation) { DiagnosticUtility.ExceptionUtility.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 (DiagnosticUtility.ShouldTraceInformation) { DiagnosticUtility.ExceptionUtility.TraceHandledException(exception, TraceEventType.Information); } } catch (CommunicationException exception) { if (DiagnosticUtility.ShouldTraceInformation) { DiagnosticUtility.ExceptionUtility.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, 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(IAsyncResult result) { WriteAsyncResult.End(result); } 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 // [....], 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 // [....], 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); } 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 // [....], transferring exception to caller catch (Exception e) { if (DiagnosticUtility.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); } unsafe void OnAsyncWriteComplete(bool haveResult, int error, int numBytes) { WriteAsyncResult result = this.writeAsyncResult; this.writeAsyncResult = null; if (result == null) { DiagnosticUtility.DebugAssert("Write completed with no WriteAsyncResult available."); throw DiagnosticUtility.ExceptionUtility.ThrowHelperInternal(false); } 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 != result.WriteSize) { 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 // [....], transferring exception to another thread catch (Exception e) { if (DiagnosticUtility.IsFatal(e)) { throw; } writeException = e; } finally { this.isWriteOutstanding = false; WriteIOCompleted(); ExitWritingState(); } } result.Complete(false, writeException); } 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(); } } } } DiagnosticUtility.DebugAssert(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); } } unsafe void StartReadZero() { lock (this.readLock) { ValidateEnterReadingState(false); StartSyncRead(ZeroBuffer, 0, 1); EnterReadingState(); } } unsafe void StartWriteZero(TimeSpan timeout) { FinishPendingWrite(timeout); lock (this.writeLock) { ValidateEnterWritingState(false); StartSyncWrite(ZeroBuffer, 0, 0); EnterWritingState(); } } public bool Validate(Uri uri) { return true; } 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. 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); } } 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(); DiagnosticUtility.DebugAssert(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]); } unsafe void StartSyncRead(byte[] buffer, int offset, int size, ref object holder) { if (this.isReadOutstanding) { DiagnosticUtility.DebugAssert("StartSyncRead called when read I/O was already pending."); throw DiagnosticUtility.ExceptionUtility.ThrowHelperInternal(false); } 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); } } } 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. 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]); } unsafe void StartSyncWrite(byte[] buffer, int offset, int size, ref object holder) { if (this.isWriteOutstanding) { DiagnosticUtility.DebugAssert("StartSyncWrite called when write I/O was already pending."); throw DiagnosticUtility.ExceptionUtility.ThrowHelperInternal(false); } 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]); } 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. 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. 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. 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 WriteAsyncResult : AsyncResult { int writeSize; public WriteAsyncResult(AsyncCallback callback, object state, int writeSize) : base(callback, state) { this.writeSize = writeSize; } public new void Complete(bool completedSynchronously, Exception e) { base.Complete(completedSynchronously, e); } public new void Complete(bool completedSynchronously) { base.Complete(completedSynchronously); } public static void End(IAsyncResult result) { AsyncResult.End (result); } public int WriteSize { get { return this.writeSize; } } } } class PipeConnectionInitiator : IConnectionInitiator { bool includeSecurityIdentity; int bufferSize; public PipeConnectionInitiator(bool includeSecurityIdentity, int bufferSize) { this.includeSecurityIdentity = includeSecurityIdentity; this.bufferSize = bufferSize; } 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) { DiagnosticUtility.DiagnosticTrace.TraceEvent( TraceEventType.Information, TraceCode.FailedPipeConnect, SR.GetString( SR.TraceCodeFailedPipeConnect, timeoutHelper.RemainingTime(), remoteUri)); } } } return connection; } internal static string GetPipeName(Uri uri) { // 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 }; for (int i = 0; i < hostChoices.Length; i++) { for (int iGlobal = 0; iGlobal < globalChoices.Length; iGlobal++) { // walk up the path hierarchy, looking for first match string path = PipeUri.GetPath(uri); while (path.Length > 0) { string sharedMemoryName = PipeUri.BuildSharedMemoryName(hostChoices[i], path, globalChoices[iGlobal]); try { PipeSharedMemory sharedMemory = PipeSharedMemory.Open(sharedMemoryName, uri); if (sharedMemory != null) { try { string pipeName = sharedMemory.PipeName; if (pipeName != null) { 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); } } } throw DiagnosticUtility.ExceptionUtility.ThrowHelperError( new EndpointNotFoundException(SR.GetString(SR.EndpointNotFound, uri.AbsoluteUri), new PipeException(SR.GetString(SR.PipeEndpointNotFound, uri.AbsoluteUri)))); } 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, new StringTraceRecord("Uri", remoteUri.ToString()), this, null); } resolvedAddress = GetPipeName(remoteUri); backoffHelper = new BackoffTimeoutHelper(TimeoutHelper.ReserveTimeAtEnd(timeout), TimeSpan.FromMinutes(5)); } 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; if (includeSecurityIdentity) { flags |= UnsafeNativeMethods.SECURITY_QOS_PRESENT | UnsafeNativeMethods.SECURITY_IDENTIFICATION; } 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)); } } class ConnectAsyncResult : AsyncResult { PipeConnectionInitiator parent; Uri remoteUri; string resolvedAddress; BackoffTimeoutHelper backoffHelper; TimeoutHelper timeoutHelper; IConnection connection; static WaitCallback 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 WaitCallback(OnWaitComplete); } this.backoffHelper.WaitAndBackoff(waitCompleteCallback, this); } return completed; } public static IConnection End(IAsyncResult result) { ConnectAsyncResult thisPtr = AsyncResult.End (result); return thisPtr.connection; } static void OnWaitComplete(object state) { Exception exception = null; ConnectAsyncResult thisPtr = (ConnectAsyncResult)state; bool completeSelf = true; try { if (DiagnosticUtility.ShouldTraceInformation) { DiagnosticUtility.DiagnosticTrace.TraceEvent( TraceEventType.Information, TraceCode.FailedPipeConnect, SR.GetString( SR.TraceCodeFailedPipeConnect, thisPtr.timeoutHelper.RemainingTime(), thisPtr.remoteUri)); } completeSelf = thisPtr.ConnectAndWait(); } catch (Exception e) { if (DiagnosticUtility.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 pendingAccepts; bool anyPipesCreated; PipeSharedMemory sharedMemory; List allowedSids; bool useCompletionPort; int maxInstances; public PipeConnectionListener(Uri pipeUri, HostNameComparisonMode hostNameComparisonMode, int bufferSize, List allowedSids, bool useCompletionPort, int maxConnections) { PipeUri.Validate(pipeUri); this.pipeUri = pipeUri; this.hostNameComparisonMode = hostNameComparisonMode; this.allowedSids = allowedSids; this.bufferSize = bufferSize; pendingAccepts = new List (); 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); } } unsafe PipeHandle CreatePipe() { int openMode = UnsafeNativeMethods.PIPE_ACCESS_DUPLEX | UnsafeNativeMethods.FILE_FLAG_OVERLAPPED; if (!anyPipesCreated) { openMode |= UnsafeNativeMethods.FILE_FLAG_FIRST_PIPE_INSTANCE; } byte[] binarySecurityDescriptor = SecurityDescriptorHelper.FromSecurityIdentifiers(allowedSids, UnsafeNativeMethods.GENERIC_READ | UnsafeNativeMethods.GENERIC_WRITE); PipeHandle pipeHandle; int error; fixed (byte* pinnedSecurityDescriptor = binarySecurityDescriptor) { UnsafeNativeMethods.SECURITY_ATTRIBUTES securityAttributes = new UnsafeNativeMethods.SECURITY_ATTRIBUTES(); securityAttributes.lpSecurityDescriptor = (IntPtr)pinnedSecurityDescriptor; pipeHandle = UnsafeNativeMethods.CreateNamedPipe(sharedMemory.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)); } } 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) { DiagnosticUtility.DebugAssert(this.pendingAccepts.Contains(pendingAccept), "An unknown PendingAccept is removing itself."); this.pendingAccepts.Remove(pendingAccept); } } class PendingAccept : AsyncResult { PipeHandle pipeHandle; PipeHandle result; OverlappedIOCompleteCallback onAcceptComplete; static WaitCallback onStartAccept; OverlappedContext overlapped; bool isBoundToCompletionPort; PipeConnectionListener listener; 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 (!Thread.CurrentThread.IsThreadPoolThread) { if (onStartAccept == null) { onStartAccept = new WaitCallback(OnStartAccept); } IOThreadScheduler.ScheduleCallback(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); } 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 race with Abort can cause PipeHandle to throw this. DiagnosticUtility.DebugAssert(this.result == null, "Got an ObjectDisposedException but not an Abort!"); if (DiagnosticUtility.ShouldTraceInformation) { DiagnosticUtility.ExceptionUtility.TraceHandledException(exception, TraceEventType.Information); } completeSelf = true; } finally { if (completeSelf) { this.overlapped.CancelAsyncOperation(); this.overlapped.Free(); } } } #pragma warning suppress 56500 // [....], transferring exception to another thread catch (Exception e) { if (DiagnosticUtility.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 (this); return this.result; } unsafe void OnAcceptComplete(bool haveResult, int error, int numBytes) { this.listener.RemovePendingAccept(this); if (!haveResult) { // No race 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 (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 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 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); if (allowedSids == null) { dacl.AddAccess(AccessControlType.Allow, new SecurityIdentifier(WellKnownSidType.WorldSid, null), accessRights, InheritanceFlags.None, PropagationFlags.None); } else { for (int i = 0; i < allowedSids.Count; i++) { SecurityIdentifier allowedSid = allowedSids[i]; dacl.AddAccess(AccessControlType.Allow, allowedSid, accessRights, InheritanceFlags.None, PropagationFlags.None); } } dacl.AddAccess(AccessControlType.Allow, new SecurityIdentifier(WellKnownSidType.CreatorOwnerSid, null), 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; } } unsafe class PipeSharedMemory : IDisposable { SafeFileMappingHandle fileMapping; string pipeName; 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 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)); } } public unsafe static bool TryCreate(List allowedSids, Uri pipeUri, string sharedMemoryName, out PipeSharedMemory result) { Guid pipeGuid = Guid.NewGuid(); string pipeName = BuildPipeName(pipeGuid); byte[] binarySecurityDescriptor = SecurityDescriptorHelper.FromSecurityIdentifiers(allowedSids, UnsafeNativeMethods.GENERIC_READ); 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; return true; } finally { if (disposeSharedMemory) { pipeSharedMemory.Dispose(); } } } 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 { get { if (pipeName == null) { SafeViewOfFileHandle view = GetView(false); try { SharedMemoryContents* contents = (SharedMemoryContents*) view.DangerousGetHandle(); if (contents->isInitialized) { Thread.MemoryBarrier(); pipeName = BuildPipeName(contents->pipeGuid); } } finally { view.Close(); } } return pipeName; } } 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(Guid guid) { return "\\\\.\\pipe\\" + guid.ToString(); } [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); } public 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) { // we don't need to check fips for our useage, so create a Managed SHA1 directly // avoids IdentityModel faulting into the reference set. using (HashAlgorithm hash = new SHA1Managed()) { 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(); } 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)); } } } } // File provided for Reference Use Only by Microsoft Corporation (c) 2007. // Copyright (c) Microsoft Corporation. All rights reserved.
Link Menu
This book is available now!
Buy at Amazon US or
Buy at Amazon UK
- GraphicsState.cs
- XmlSerializerNamespaces.cs
- SqlXml.cs
- ProcessStartInfo.cs
- XmlElementAttributes.cs
- UriSectionData.cs
- DataGridTextBox.cs
- BufferedStream.cs
- PeerObject.cs
- Rectangle.cs
- BitmapMetadata.cs
- WebResponse.cs
- DataFieldCollectionEditor.cs
- ThousandthOfEmRealPoints.cs
- XmlBinaryReaderSession.cs
- ControlParameter.cs
- DBParameter.cs
- AnnotationHelper.cs
- ConstraintManager.cs
- DesignerCategoryAttribute.cs
- FtpWebRequest.cs
- PreservationFileReader.cs
- TextOptionsInternal.cs
- SqlInfoMessageEvent.cs
- DataGridViewCellFormattingEventArgs.cs
- GeneralTransformCollection.cs
- EditorPartChrome.cs
- CustomWebEventKey.cs
- ProtocolsSection.cs
- OutputBuffer.cs
- SharedDp.cs
- RoutedEventHandlerInfo.cs
- ADMembershipProvider.cs
- FaultDesigner.cs
- FactoryGenerator.cs
- InternalBufferManager.cs
- EventDescriptor.cs
- TextDocumentView.cs
- SqlCachedBuffer.cs
- DeferredElementTreeState.cs
- XmlDocumentType.cs
- OutputCacheEntry.cs
- LineServices.cs
- UriParserTemplates.cs
- UrlPropertyAttribute.cs
- MaskedTextBox.cs
- TextServicesHost.cs
- OutputScope.cs
- CommandTreeTypeHelper.cs
- Size3D.cs
- PropertyValueUIItem.cs
- PenThreadPool.cs
- CodePageUtils.cs
- NativeMethods.cs
- OleDbTransaction.cs
- HyperLinkField.cs
- WebEventTraceProvider.cs
- WebHttpBindingElement.cs
- DataGridViewCellFormattingEventArgs.cs
- Int64Storage.cs
- ISFClipboardData.cs
- AppearanceEditorPart.cs
- OdbcParameterCollection.cs
- HttpStreamXmlDictionaryWriter.cs
- SynchronizationContext.cs
- MenuItemBindingCollection.cs
- DataServiceStreamResponse.cs
- StylusEditingBehavior.cs
- EncodingDataItem.cs
- ListBoxItemWrapperAutomationPeer.cs
- HMACSHA384.cs
- InertiaExpansionBehavior.cs
- SimpleFieldTemplateFactory.cs
- ClientTargetSection.cs
- MinimizableAttributeTypeConverter.cs
- BCryptNative.cs
- SqlInternalConnectionTds.cs
- WindowsAltTab.cs
- InputGestureCollection.cs
- FontResourceCache.cs
- BufferBuilder.cs
- Int32CAMarshaler.cs
- CqlLexer.cs
- XmlSchemaInclude.cs
- ColumnHeaderConverter.cs
- ObfuscateAssemblyAttribute.cs
- HtmlMobileTextWriter.cs
- Codec.cs
- PeerObject.cs
- AbsoluteQuery.cs
- ClientOptions.cs
- DefaultEventAttribute.cs
- XsltQilFactory.cs
- SHA256Managed.cs
- QilStrConcat.cs
- ProcessHost.cs
- DocumentSequence.cs
- IndicCharClassifier.cs
- SHA1CryptoServiceProvider.cs
- AppearanceEditorPart.cs