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