//
// System.Runtime.Remoting.Channels.Ipc.Win32.NamedPipeSocket.cs
//
// Author: Robert Jordan (robertj@gmx.net)
//
// Copyright (C) 2005 Novell, Inc (http://www.novell.com)
//
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.Remoting.Messaging;
using System.Text;
namespace System.Runtime.Remoting.Channels.Ipc.Win32
{
///
/// Implements a local Named Pipe socket.
///
internal class NamedPipeSocket : IDisposable
{
IntPtr hPipe;
///
/// Creates a new socket instance form the specified local Named Pipe handle.
///
/// The handle.
internal NamedPipeSocket(IntPtr hPipe)
{
this.hPipe = hPipe;
this.info = new NamedPipeSocketInfo(hPipe);
}
~NamedPipeSocket()
{
((IDisposable)this).Dispose();
}
///
/// Gets the NamedPipeSocketInfo of this instance.
///
///
public NamedPipeSocketInfo Info
{
get
{
return info;
}
}
NamedPipeSocketInfo info;
///
/// Closes the socket.
///
public void Close()
{
((IDisposable)this).Dispose();
}
///
/// Disposes the object.
///
void IDisposable.Dispose()
{
if (hPipe != IntPtr.Zero)
{
try
{
// disconnect the pipe
if (Info.IsServer)
{
NamedPipeHelper.FlushFileBuffers(hPipe);
NamedPipeHelper.DisconnectNamedPipe(hPipe);
}
}
catch (NamedPipeException)
{
}
NamedPipeHelper.CloseHandle(hPipe);
hPipe = IntPtr.Zero;
GC.SuppressFinalize(this);
}
}
///
/// Returns the stream used to send and receive data.
///
///
public Stream GetStream()
{
if (hPipe == IntPtr.Zero)
throw new ObjectDisposedException(GetType().FullName);
return stream == null
? stream = new NamedPipeStream(this, false)
: stream;
}
Stream stream;
///
/// Returns the stream used to send and receive data. The stream disposes
/// the socket on close.
///
///
public Stream GetClosingStream()
{
if (hPipe == IntPtr.Zero)
throw new ObjectDisposedException(GetType().FullName);
return new NamedPipeStream(this, true);
}
///
/// Flushes the socket.
///
public void Flush()
{
if (hPipe == IntPtr.Zero)
throw new ObjectDisposedException(GetType().FullName);
NamedPipeHelper.FlushFileBuffers(hPipe);
}
///
/// Receives the specified number of bytes from a socket into
/// the specified offset position of the receive buffer.
///
/// An array of type Byte that is the storage
/// location for received data.
/// The location in buffer to store the received data.
/// The number of bytes to receive.
/// The number of bytes received.
public int Receive(byte[] buffer, int offset, int count)
{
if (hPipe == IntPtr.Zero)
throw new ObjectDisposedException(GetType().FullName);
if (buffer == null)
throw new ArgumentNullException("buffer");
if (offset < 0 || count < 0)
throw new ArgumentOutOfRangeException("offset and/or count");
if (buffer.Length - offset < count)
throw new ArgumentException();
uint read = 0;
GCHandle gch = GCHandle.Alloc(buffer, GCHandleType.Pinned);
try
{
bool res = NamedPipeHelper.ReadFile(
hPipe,
Marshal.UnsafeAddrOfPinnedArrayElement(buffer, offset),
(uint) count,
out read,
IntPtr.Zero
);
if (!res && read == 0) throw new NamedPipeException();
return (int) read;
}
finally
{
gch.Free();
}
}
delegate int ReceiveMethod(byte[] buffer, int offset, int count);
public IAsyncResult BeginReceive(byte[] buffer, int offset, int count,
AsyncCallback callback, object state)
{
return new ReceiveMethod(Receive).BeginInvoke(buffer, offset, count, callback, state);
}
public int EndReceive(IAsyncResult asyncResult)
{
AsyncResult ar = asyncResult as AsyncResult;
return ((ReceiveMethod)ar.AsyncDelegate).EndInvoke(asyncResult);
}
///
/// Sends the specified number of bytes of data to a connected socket,
/// starting at the specified offset.
///
/// An array of type Byte that contains the data to be sent.
/// The position in the data buffer at which to begin sending data.
/// The number of bytes to send.
/// The number of bytes sent.
public int Send(byte[] buffer, int offset, int count)
{
if (hPipe == IntPtr.Zero)
throw new ObjectDisposedException(GetType().FullName);
if (buffer == null)
throw new ArgumentNullException("buffer");
if (offset < 0 || count < 0)
throw new ArgumentOutOfRangeException("offset and/or count");
if (buffer.Length - offset < count)
throw new ArgumentException();
uint written = 0;
GCHandle gch = GCHandle.Alloc(buffer, GCHandleType.Pinned);
try
{
bool res = NamedPipeHelper.WriteFile(
hPipe,
Marshal.UnsafeAddrOfPinnedArrayElement(buffer, offset),
(uint) count,
out written,
IntPtr.Zero
);
if (!res) throw new NamedPipeException();
return (int) written;
}
finally
{
gch.Free();
}
}
delegate int SendMethod(byte[] buffer, int offset, int count);
public IAsyncResult BeginSend(byte[] buffer, int offset, int count,
AsyncCallback callback, object state)
{
return new SendMethod(Send).BeginInvoke(buffer, offset, count, callback, state);
}
public int EndSend(IAsyncResult asyncResult)
{
AsyncResult ar = asyncResult as AsyncResult;
return ((SendMethod)ar.AsyncDelegate).EndInvoke(asyncResult);
}
///
/// Returns the current NamedPipeSocketState of this instance.
///
///
public NamedPipeSocketState GetSocketState()
{
if (hPipe == IntPtr.Zero)
throw new ObjectDisposedException(GetType().FullName);
return new NamedPipeSocketState(hPipe);
}
///
/// Impersonates the client.
///
public void Impersonate()
{
if (hPipe == IntPtr.Zero)
throw new ObjectDisposedException(GetType().FullName);
if (Info.IsServer)
if (!NamedPipeHelper.ImpersonateNamedPipeClient(hPipe))
throw new NamedPipeException();
}
///
/// Reverts the impersonation.
///
public static bool RevertToSelf()
{
return NamedPipeHelper.RevertToSelf();
}
}
///
/// Represents local Named Pipe informations.
///
internal class NamedPipeSocketInfo
{
public readonly int Flags;
public readonly int OutBufferSize;
public readonly int InBufferSize;
public readonly int MaxInstances;
public bool IsServer
{
get
{
return (Flags & NamedPipeHelper.PIPE_SERVER_END) != 0;
}
}
internal NamedPipeSocketInfo(IntPtr hPipe)
{
bool res = NamedPipeHelper.GetNamedPipeInfo(
hPipe,
out Flags,
out OutBufferSize,
out InBufferSize,
out MaxInstances
);
if (!res)
{
throw new NamedPipeException();
}
}
}
///
/// Represents local Named Pipe state informations.
///
internal class NamedPipeSocketState
{
public readonly int State;
public readonly int CurrentInstances;
public readonly int MaxCollectionCount;
public readonly int CollectDataTimeout;
public readonly string UserName;
internal NamedPipeSocketState(IntPtr hPipe)
{
StringBuilder userName = new StringBuilder(256);
bool res = NamedPipeHelper.GetNamedPipeHandleState(
hPipe,
out State,
out CurrentInstances,
out MaxCollectionCount,
out CollectDataTimeout,
userName,
userName.Capacity
);
if (res)
{
UserName = userName.ToString();
}
else
{
throw new NamedPipeException();
}
}
}
}