Merge branch 'master' of github.com:mono/mono
[mono.git] / mcs / class / System / System.Net.Sockets / SocketAsyncEventArgs.cs
1 // System.Net.Sockets.SocketAsyncEventArgs.cs
2 //
3 // Authors:
4 //      Marek Habersack (mhabersack@novell.com)
5 //
6 // Copyright (c) 2008 Novell, Inc. (http://www.novell.com)
7 //
8
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 // 
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 // 
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29 #if NET_2_0
30 using System;
31 using System.Collections.Generic;
32 using System.Reflection;
33 using System.Security;
34 using System.Threading;
35 #if MOONLIGHT && !INSIDE_SYSTEM
36 using System.Net.Policy;
37 #endif
38
39 namespace System.Net.Sockets
40 {
41         public class SocketAsyncEventArgs : EventArgs, IDisposable
42         {
43 #if MOONLIGHT || NET_4_0
44                 public Exception ConnectByNameError { get; internal set; }
45 #endif
46
47                 public event EventHandler<SocketAsyncEventArgs> Completed;
48
49                 IList <ArraySegment <byte>> _bufferList;
50                 
51                 public Socket AcceptSocket { get; set; }
52                 public byte[] Buffer { get; private set; }
53
54                 [MonoTODO ("not supported in all cases")]
55                 public IList<ArraySegment<byte>> BufferList {
56                         get { return _bufferList; }
57                         set {
58                                 if (Buffer != null && value != null)
59                                         throw new ArgumentException ("Buffer and BufferList properties cannot both be non-null.");
60                                 _bufferList = value;
61                         }
62                 }
63
64                 public int BytesTransferred { get; private set; }
65                 public int Count { get; private set; }
66                 public bool DisconnectReuseSocket { get; set; }
67                 public SocketAsyncOperation LastOperation { get; private set; }
68                 public int Offset { get; private set; }
69                 public EndPoint RemoteEndPoint { get; set; }
70 #if !NET_2_1
71                 public IPPacketInformation ReceiveMessageFromPacketInfo { get; private set; }
72                 public SendPacketsElement[] SendPacketsElements { get; set; }
73                 public TransmitFileOptions SendPacketsFlags { get; set; }
74 #endif
75                 [MonoTODO ("unused property")]
76                 public int SendPacketsSendSize { get; set; }
77                 public SocketError SocketError { get; set; }
78                 public SocketFlags SocketFlags { get; set; }
79                 public object UserToken { get; set; }
80
81 #if MOONLIGHT && !INSIDE_SYSTEM
82                 private SocketClientAccessPolicyProtocol policy_protocol;
83
84                 [MonoTODO ("Only TCP is currently supported by Moonlight")]
85                 public SocketClientAccessPolicyProtocol SocketClientAccessPolicyProtocol {
86                         get { return policy_protocol; }
87                         set {
88                                 if ((value != SocketClientAccessPolicyProtocol.Tcp) && (value != SocketClientAccessPolicyProtocol.Http))
89                                         throw new ArgumentException ("Invalid value");
90                                 policy_protocol = value;
91                         }
92                 }
93 #endif
94
95                 Socket curSocket;
96 #if NET_2_1
97                 public Socket ConnectSocket {
98                         get {
99                                 switch (SocketError) {
100                                 case SocketError.AccessDenied:
101                                         return null;
102                                 default:
103                                         return curSocket;
104                                 }
105                         }
106                 }
107
108                 internal bool PolicyRestricted { get; private set; }
109
110                 internal SocketAsyncEventArgs (bool policy) : 
111                         this ()
112                 {
113                         PolicyRestricted = policy;
114                 }
115 #endif
116                 
117                 public SocketAsyncEventArgs ()
118                 {
119                         AcceptSocket = null;
120                         Buffer = null;
121                         BufferList = null;
122                         BytesTransferred = 0;
123                         Count = 0;
124                         DisconnectReuseSocket = false;
125                         LastOperation = SocketAsyncOperation.None;
126                         Offset = 0;
127                         RemoteEndPoint = null;
128 #if !NET_2_1
129                         SendPacketsElements = null;
130                         SendPacketsFlags = TransmitFileOptions.UseDefaultWorkerThread;
131 #endif
132                         SendPacketsSendSize = -1;
133                         SocketError = SocketError.Success;
134                         SocketFlags = SocketFlags.None;
135                         UserToken = null;
136
137 #if MOONLIGHT && !INSIDE_SYSTEM
138                         policy_protocol = SocketClientAccessPolicyProtocol.Tcp;
139 #endif
140                 }
141
142                 ~SocketAsyncEventArgs ()
143                 {
144                         Dispose (false);
145                 }
146
147                 void Dispose (bool disposing)
148                 {
149                         Socket acceptSocket = AcceptSocket;
150                         if (acceptSocket != null)
151                                 acceptSocket.Close ();
152
153                         if (disposing)
154                                 GC.SuppressFinalize (this);
155                 }               
156
157                 public void Dispose ()
158                 {
159                         Dispose (true);
160                 }
161                 
162                 protected virtual void OnCompleted (SocketAsyncEventArgs e)
163                 {
164                         if (e == null)
165                                 return;
166                         
167                         EventHandler<SocketAsyncEventArgs> handler = e.Completed;
168                         if (handler != null)
169                                 handler (e.curSocket, e);
170                 }
171
172                 public void SetBuffer (int offset, int count)
173                 {
174                         SetBufferInternal (Buffer, offset, count);
175                 }
176
177                 public void SetBuffer (byte[] buffer, int offset, int count)
178                 {
179                         SetBufferInternal (buffer, offset, count);
180                 }
181
182                 void SetBufferInternal (byte[] buffer, int offset, int count)
183                 {
184                         if (buffer != null) {
185                                 if (BufferList != null)
186                                         throw new ArgumentException ("Buffer and BufferList properties cannot both be non-null.");
187                                 
188                                 int buflen = buffer.Length;
189                                 if (offset < 0 || (offset != 0 && offset >= buflen))
190                                         throw new ArgumentOutOfRangeException ("offset");
191
192                                 if (count < 0 || count > buflen - offset)
193                                         throw new ArgumentOutOfRangeException ("count");
194
195                                 Count = count;
196                                 Offset = offset;
197                         }
198                         Buffer = buffer;
199                 }
200
201 #region Internals
202                 void ReceiveCallback ()
203                 {
204                         SocketError = SocketError.Success;
205                         LastOperation = SocketAsyncOperation.Receive;
206                         SocketError error = SocketError.Success;
207
208                         if (!curSocket.Connected) {
209                                 SocketError = SocketError.NotConnected;
210                                 return;
211                         }
212                         
213                         try {
214                                 // FIXME: this does not support using BufferList
215                                 BytesTransferred = curSocket.Receive_nochecks (Buffer, Offset, Count, SocketFlags, out error);
216                         } finally {
217                                 SocketError = error;
218                                 OnCompleted (this);
219                         }
220                 }
221
222                 void ConnectCallback ()
223                 {
224                         LastOperation = SocketAsyncOperation.Connect;
225                         SocketError error = SocketError.AccessDenied;
226                         try {
227 #if MOONLIGHT || NET_4_0
228                                 // Connect to the first address that match the host name, like:
229                                 // http://blogs.msdn.com/ncl/archive/2009/07/20/new-ncl-features-in-net-4-0-beta-2.aspx
230                                 // while skipping entries that do not match the address family
231                                 DnsEndPoint dep = (RemoteEndPoint as DnsEndPoint);
232                                 if (dep != null) {
233                                         IPAddress[] addresses = Dns.GetHostAddresses (dep.Host);
234                                         foreach (IPAddress addr in addresses) {
235                                                 try {
236                                                         if (curSocket.AddressFamily == addr.AddressFamily) {
237                                                                 error = TryConnect (new IPEndPoint (addr, dep.Port));
238                                                                 if (error == SocketError.Success) {
239                                                                         ConnectByNameError = null;
240                                                                         break;
241                                                                 }
242                                                         }
243                                                 }
244                                                 catch (SocketException se) {
245                                                         ConnectByNameError = se;
246                                                         error = SocketError.AccessDenied;
247                                                 }
248                                         }
249                                 } else {
250                                         ConnectByNameError = null;
251                                         error = TryConnect (RemoteEndPoint);
252                                 }
253 #else
254                                 error = TryConnect (RemoteEndPoint);
255 #endif
256                         } finally {
257                                 SocketError = error;
258                                 OnCompleted (this);
259                         }
260                 }
261
262                 SocketError TryConnect (EndPoint endpoint)
263                 {
264                         curSocket.Connected = false;
265                         SocketError error = SocketError.Success;
266 #if MOONLIGHT && !INSIDE_SYSTEM
267                         // if we're not downloading a socket policy then check the policy
268                         // and if we're not running with elevated permissions (SL4 OoB option)
269                         if (!PolicyRestricted && !SecurityManager.HasElevatedPermissions) {
270                                 error = SocketError.AccessDenied;
271                                 if (!CrossDomainPolicyManager.CheckEndPoint (endpoint, policy_protocol)) {
272                                         return error;
273                                 }
274                                 error = SocketError.Success;
275                         }
276 #endif
277                         try {
278 #if !NET_2_1
279                                 if (!curSocket.Blocking) {
280                                         int success;
281                                         curSocket.Poll (-1, SelectMode.SelectWrite, out success);
282                                         error = (SocketError)success;
283                                         if (success == 0)
284                                                 curSocket.Connected = true;
285                                         else
286                                                 return error;
287                                 } else
288 #endif
289                                 {
290                                         curSocket.seed_endpoint = endpoint;
291                                         curSocket.Connect (endpoint);
292                                         curSocket.Connected = true;
293                                 }
294                         } catch (SocketException se){
295                                 error = se.SocketErrorCode;
296                         }
297                         return error;
298                 }
299
300                 void SendCallback ()
301                 {
302                         SocketError = SocketError.Success;
303                         LastOperation = SocketAsyncOperation.Send;
304                         SocketError error = SocketError.Success;
305
306                         if (!curSocket.Connected) {
307                                 SocketError = SocketError.NotConnected;
308                                 return;
309                         }
310
311                         try {
312                                 if (Buffer != null) {
313                                         BytesTransferred = curSocket.Send_nochecks (Buffer, Offset, Count, SocketFlags.None, out error);
314                                 } else if (BufferList != null) {
315                                         BytesTransferred = 0;
316                                         foreach (ArraySegment<byte> asb in BufferList) {
317                                                 BytesTransferred += curSocket.Send_nochecks (asb.Array, asb.Offset, asb.Count, 
318                                                         SocketFlags.None, out error);
319                                                 if (error != SocketError.Success)
320                                                         break;
321                                         }
322                                 }
323                         } finally {
324                                 SocketError = error;
325                                 OnCompleted (this);
326                         }
327                 }
328 #if !NET_2_1
329                 void AcceptCallback ()
330                 {
331                         SocketError = SocketError.Success;
332                         LastOperation = SocketAsyncOperation.Accept;
333                         try {
334                                 curSocket.Accept (AcceptSocket);
335                         } catch (SocketException ex) {
336                                 SocketError = ex.SocketErrorCode;
337                                 throw;
338                         } finally {
339                                 OnCompleted (this);
340                         }
341                 }
342
343                 void DisconnectCallback ()
344                 {
345                         SocketError = SocketError.Success;
346                         LastOperation = SocketAsyncOperation.Disconnect;
347
348                         try {
349                                 curSocket.Disconnect (DisconnectReuseSocket);
350                         } catch (SocketException ex) {
351                                 SocketError = ex.SocketErrorCode;
352                                 throw;
353                         } finally {
354                                 OnCompleted (this);
355                         }
356                 }
357
358                 void ReceiveFromCallback ()
359                 {
360                         SocketError = SocketError.Success;
361                         LastOperation = SocketAsyncOperation.ReceiveFrom;
362
363                         try {
364                                 EndPoint ep = RemoteEndPoint;
365                                 if (Buffer != null) {
366                                         BytesTransferred = curSocket.ReceiveFrom_nochecks (Buffer, Offset, Count, SocketFlags, ref ep);
367                                 } else if (BufferList != null) {
368                                         throw new NotImplementedException ();
369                                 }
370                         } catch (SocketException ex) {
371                                 SocketError = ex.SocketErrorCode;
372                                 throw;
373                         } finally {
374                                 OnCompleted (this);
375                         }
376                 }
377
378                 void SendToCallback ()
379                 {
380                         SocketError = SocketError.Success;
381                         LastOperation = SocketAsyncOperation.SendTo;
382                         int total = 0;
383                         
384                         try {
385                                 int count = Count;
386
387                                 while (total < count)
388                                         total += curSocket.SendTo_nochecks (Buffer, Offset, count, SocketFlags, RemoteEndPoint);
389                                 BytesTransferred = total;
390                         } catch (SocketException ex) {
391                                 SocketError = ex.SocketErrorCode;
392                                 throw;
393                         } finally {
394                                 OnCompleted (this);
395                         }
396                 }
397 #endif
398                 internal void DoOperation (SocketAsyncOperation operation, Socket socket)
399                 {
400                         ThreadStart callback;
401                         curSocket = socket;
402                         
403                         switch (operation) {
404 #if !NET_2_1
405                                 case SocketAsyncOperation.Accept:
406                                         callback = new ThreadStart (AcceptCallback);
407                                         break;
408
409                                 case SocketAsyncOperation.Disconnect:
410                                         callback = new ThreadStart (DisconnectCallback);
411                                         break;
412
413                                 case SocketAsyncOperation.ReceiveFrom:
414                                         callback = new ThreadStart (ReceiveFromCallback);
415                                         break;
416
417                                 case SocketAsyncOperation.SendTo:
418                                         callback = new ThreadStart (SendToCallback);
419                                         break;
420 #endif
421                                 case SocketAsyncOperation.Receive:
422                                         callback = new ThreadStart (ReceiveCallback);
423                                         break;
424
425                                 case SocketAsyncOperation.Connect:
426                                         callback = new ThreadStart (ConnectCallback);
427                                         break;
428
429                                 case SocketAsyncOperation.Send:
430                                         callback = new ThreadStart (SendCallback);
431                                         break;
432                                 
433                                 default:
434                                         throw new NotSupportedException ();
435                         }
436
437                         Thread t = new Thread (callback);
438                         t.IsBackground = true;
439                         t.Start ();
440                 }
441 #endregion
442         }
443 }
444 #endif