Merge pull request #1500 from criteo-forks/criteo
[mono.git] / mcs / class / System.Runtime.Remoting / System.Runtime.Remoting.Channels.Ipc.Win32 / NamedPipeSocket.cs
1 //
2 // System.Runtime.Remoting.Channels.Ipc.Win32.NamedPipeSocket.cs
3 //
4 // Author: Robert Jordan (robertj@gmx.net)
5 //
6 // Copyright (C) 2005 Novell, Inc (http://www.novell.com)
7 //
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 // 
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 // 
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28
29
30 using System;
31 using System.IO;
32 using System.Runtime.InteropServices;
33 using System.Runtime.Remoting.Messaging;
34 using System.Text;
35
36 namespace System.Runtime.Remoting.Channels.Ipc.Win32
37 {
38     /// <summary>
39     /// Implements a local Named Pipe socket.
40     /// </summary>
41     internal class NamedPipeSocket : IDisposable
42     {
43         IntPtr hPipe;
44
45         /// <summary>
46         /// Creates a new socket instance form the specified local Named Pipe handle.
47         /// </summary>
48         /// <param name="hPipe">The handle.</param>
49         internal NamedPipeSocket(IntPtr hPipe)
50         {
51             this.hPipe = hPipe;
52             this.info = new NamedPipeSocketInfo(hPipe);
53         }
54
55         ~NamedPipeSocket() 
56         {
57             ((IDisposable)this).Dispose();
58         }
59
60         /// <summary>
61         /// Gets the NamedPipeSocketInfo of this instance.
62         /// </summary>
63         /// <returns></returns>
64         public NamedPipeSocketInfo Info
65         {
66             get 
67             {
68                 return info;
69             }
70         }
71
72         NamedPipeSocketInfo info;
73
74         /// <summary>
75         /// Closes the socket.
76         /// </summary>
77         public void Close() 
78         {
79             ((IDisposable)this).Dispose();
80         }
81
82         /// <summary>
83         /// Disposes the object.
84         /// </summary>
85         void IDisposable.Dispose()
86         {
87             if (hPipe != IntPtr.Zero) 
88             {
89                 try 
90                 {
91                     // disconnect the pipe
92                     if (Info.IsServer) 
93                     {
94                         NamedPipeHelper.FlushFileBuffers(hPipe);
95                         NamedPipeHelper.DisconnectNamedPipe(hPipe);
96                     }
97                 }
98                 catch (NamedPipeException) 
99                 {
100                 }
101
102                 NamedPipeHelper.CloseHandle(hPipe);
103                 hPipe = IntPtr.Zero;
104                 GC.SuppressFinalize(this);
105             }
106         }
107
108         /// <summary>
109         /// Returns the stream used to send and receive data.
110         /// </summary>
111         /// <returns></returns>
112         public Stream GetStream() 
113         {
114             if (hPipe == IntPtr.Zero)
115                 throw new ObjectDisposedException(GetType().FullName);
116
117             return stream == null
118                 ? stream = new NamedPipeStream(this, false)
119                 : stream;
120         }
121
122         Stream stream;
123
124         /// <summary>
125         /// Returns the stream used to send and receive data. The stream disposes
126         /// the socket on close.
127         /// </summary>
128         /// <returns></returns>
129         public Stream GetClosingStream() 
130         {
131             if (hPipe == IntPtr.Zero)
132                 throw new ObjectDisposedException(GetType().FullName);
133             
134             return new NamedPipeStream(this, true);
135         }
136
137         /// <summary>
138         /// Flushes the socket.
139         /// </summary>
140         public void Flush() 
141         {
142             if (hPipe == IntPtr.Zero)
143                 throw new ObjectDisposedException(GetType().FullName);
144
145             NamedPipeHelper.FlushFileBuffers(hPipe);
146         }
147
148         /// <summary>
149         /// Receives the specified number of bytes from a socket into
150         /// the specified offset position of the receive buffer.
151         /// </summary>
152         /// <param name="buffer">An array of type Byte that is the storage
153         /// location for received data.</param>
154         /// <param name="offset">The location in buffer to store the received data.</param>
155         /// <param name="count">The number of bytes to receive.</param>
156         /// <returns>The number of bytes received.</returns>
157         public int Receive(byte[] buffer, int offset, int count) 
158         {
159             if (hPipe == IntPtr.Zero)
160                 throw new ObjectDisposedException(GetType().FullName);
161             if (buffer == null)
162                 throw new ArgumentNullException("buffer");
163             if (offset < 0 || count < 0)
164                 throw new ArgumentOutOfRangeException("offset and/or count");
165             if (buffer.Length - offset < count)
166                 throw new ArgumentException();
167
168             uint read = 0;
169
170             GCHandle gch  = GCHandle.Alloc(buffer, GCHandleType.Pinned);
171             try 
172             {
173                 bool res = NamedPipeHelper.ReadFile(
174                     hPipe,
175                     Marshal.UnsafeAddrOfPinnedArrayElement(buffer, offset),
176                     (uint) count,
177                     out read,
178                     IntPtr.Zero
179                     );
180
181                 if (!res && read == 0) throw new NamedPipeException();
182
183                 return (int) read;
184             }
185             finally 
186             {
187                 gch.Free();
188             }
189         }
190
191         delegate int ReceiveMethod(byte[] buffer, int offset, int count);
192
193         public IAsyncResult BeginReceive(byte[] buffer, int offset, int count,
194             AsyncCallback callback, object state)
195         {
196             return new ReceiveMethod(Receive).BeginInvoke(buffer, offset, count, callback, state);
197         }
198
199         public int EndReceive(IAsyncResult asyncResult) 
200         {
201             AsyncResult ar = asyncResult as AsyncResult;
202             return ((ReceiveMethod)ar.AsyncDelegate).EndInvoke(asyncResult);
203         }
204
205         /// <summary>
206         /// Sends the specified number of bytes of data to a connected socket,
207         /// starting at the specified offset.
208         /// </summary>
209         /// <param name="buffer">An array of type Byte that contains the data to be sent.</param>
210         /// <param name="offset">The position in the data buffer at which to begin sending data. </param>
211         /// <param name="count">The number of bytes to send.</param>
212         /// <returns>The number of bytes sent.</returns>
213         public int Send(byte[] buffer, int offset, int count) 
214         {
215             if (hPipe == IntPtr.Zero)
216                 throw new ObjectDisposedException(GetType().FullName);
217             if (buffer == null)
218                 throw new ArgumentNullException("buffer");
219             if (offset < 0 || count < 0)
220                 throw new ArgumentOutOfRangeException("offset and/or count");
221             if (buffer.Length - offset < count)
222                 throw new ArgumentException();
223
224             uint written = 0;
225
226             GCHandle gch  = GCHandle.Alloc(buffer, GCHandleType.Pinned);
227             try 
228             {
229                 bool res = NamedPipeHelper.WriteFile(
230                     hPipe,
231                     Marshal.UnsafeAddrOfPinnedArrayElement(buffer, offset),
232                     (uint) count,
233                     out written,
234                     IntPtr.Zero
235                     );
236
237                 if (!res) throw new NamedPipeException();
238                 return (int) written;
239             }
240             finally 
241             {
242                 gch.Free();
243             }
244         }
245
246         delegate int SendMethod(byte[] buffer, int offset, int count);
247
248         public IAsyncResult BeginSend(byte[] buffer, int offset, int count,
249             AsyncCallback callback, object state)
250         {
251             return new SendMethod(Send).BeginInvoke(buffer, offset, count, callback, state);
252         }
253
254         public int EndSend(IAsyncResult asyncResult) 
255         {
256             AsyncResult ar = asyncResult as AsyncResult;
257             return ((SendMethod)ar.AsyncDelegate).EndInvoke(asyncResult);
258         }
259
260         /// <summary>
261         /// Returns the current NamedPipeSocketState of this instance.
262         /// </summary>
263         /// <returns></returns>
264         public NamedPipeSocketState GetSocketState() 
265         {
266             if (hPipe == IntPtr.Zero)
267                 throw new ObjectDisposedException(GetType().FullName);
268
269             return new NamedPipeSocketState(hPipe);
270         }
271
272         /// <summary>
273         /// Impersonates the client.
274         /// </summary>
275         public void Impersonate() 
276         {
277             if (hPipe == IntPtr.Zero)
278                 throw new ObjectDisposedException(GetType().FullName);
279
280             if (Info.IsServer)
281                 if (!NamedPipeHelper.ImpersonateNamedPipeClient(hPipe))
282                     throw new NamedPipeException();
283         }
284
285         /// <summary>
286         /// Reverts the impersonation.
287         /// </summary>
288         public static bool RevertToSelf() 
289         {
290             return NamedPipeHelper.RevertToSelf();
291         }
292
293     }
294
295
296     /// <summary>
297     /// Represents local Named Pipe informations.
298     /// </summary>
299     internal class NamedPipeSocketInfo
300     {
301         public readonly int Flags;
302         public readonly int OutBufferSize;
303         public readonly int InBufferSize;
304         public readonly int MaxInstances;
305
306         public bool IsServer 
307         {
308             get 
309             {
310                 return (Flags & NamedPipeHelper.PIPE_SERVER_END) != 0;
311             }
312         }
313
314         internal NamedPipeSocketInfo(IntPtr hPipe) 
315         {
316             bool res = NamedPipeHelper.GetNamedPipeInfo(
317                 hPipe,
318                 out Flags,
319                 out OutBufferSize,
320                 out InBufferSize,
321                 out MaxInstances
322                 );
323             
324             if (!res) 
325             {
326                 throw new NamedPipeException();
327             }
328         }
329     }
330
331
332     /// <summary>
333     /// Represents local Named Pipe state informations.
334     /// </summary>
335     internal class NamedPipeSocketState 
336     {
337         public readonly int State;
338         public readonly int CurrentInstances;
339         public readonly int MaxCollectionCount;
340         public readonly int CollectDataTimeout;
341         public readonly string UserName;
342
343         internal NamedPipeSocketState(IntPtr hPipe) 
344         {
345             StringBuilder userName = new StringBuilder(256);
346             bool res = NamedPipeHelper.GetNamedPipeHandleState(
347                 hPipe,
348                 out State,
349                 out CurrentInstances,
350                 out MaxCollectionCount,
351                 out CollectDataTimeout,
352                 userName,
353                 userName.Capacity
354                 );
355             
356             if (res) 
357             {
358                 UserName = userName.ToString();
359             }
360             else 
361             {
362                 throw new NamedPipeException();
363             }
364         }
365     }
366 }
367