[socket] Reduce handle manipulations in native (#5378)
[mono.git] / mcs / class / System / System.Net.Sockets / Socket.cs
1 // System.Net.Sockets.Socket.cs
2 //
3 // Authors:
4 //      Phillip Pearson (pp@myelin.co.nz)
5 //      Dick Porter <dick@ximian.com>
6 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 //      Sridhar Kulkarni (sridharkulkarni@gmail.com)
8 //      Brian Nickel (brian.nickel@gmail.com)
9 //      Ludovic Henry (ludovic@xamarin.com)
10 //
11 // Copyright (C) 2001, 2002 Phillip Pearson and Ximian, Inc.
12 //    http://www.myelin.co.nz
13 // (c) 2004-2011 Novell, Inc. (http://www.novell.com)
14 //
15 //
16 // Permission is hereby granted, free of charge, to any person obtaining
17 // a copy of this software and associated documentation files (the
18 // "Software"), to deal in the Software without restriction, including
19 // without limitation the rights to use, copy, modify, merge, publish,
20 // distribute, sublicense, and/or sell copies of the Software, and to
21 // permit persons to whom the Software is furnished to do so, subject to
22 // the following conditions:
23 // 
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
26 // 
27 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
31 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
32 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 //
35
36 using System;
37 using System.Net;
38 using System.Collections;
39 using System.Collections.Generic;
40 using System.Runtime.CompilerServices;
41 using System.Runtime.InteropServices;
42 using System.Threading;
43 using System.Reflection;
44 using System.IO;
45 using System.Net.Configuration;
46 using System.Text;
47 using System.Timers;
48 using System.Net.NetworkInformation;
49
50 namespace System.Net.Sockets 
51 {
52         public partial class Socket : IDisposable
53         {
54                 const int SOCKET_CLOSED_CODE = 10004;
55                 const string TIMEOUT_EXCEPTION_MSG = "A connection attempt failed because the connected party did not properly respond" +
56                         "after a period of time, or established connection failed because connected host has failed to respond";
57
58                 /* true if we called Close_internal */
59                 bool is_closed;
60
61                 bool is_listening;
62                 bool useOverlappedIO;
63
64                 int linger_timeout;
65
66                 AddressFamily addressFamily;
67                 SocketType socketType;
68                 ProtocolType protocolType;
69
70                 /* the field "m_Handle" is looked up by name by the runtime */
71                 internal SafeSocketHandle m_Handle;
72
73                 /*
74                  * This EndPoint is used when creating new endpoints. Because
75                  * there are many types of EndPoints possible,
76                  * seed_endpoint.Create(addr) is used for creating new ones.
77                  * As such, this value is set on Bind, SentTo, ReceiveFrom,
78                  * Connect, etc.
79                  */
80                 internal EndPoint seed_endpoint = null;
81
82                 internal SemaphoreSlim ReadSem = new SemaphoreSlim (1, 1);
83                 internal SemaphoreSlim WriteSem = new SemaphoreSlim (1, 1);
84
85                 internal bool is_blocking = true;
86                 internal bool is_bound;
87
88                 /* When true, the socket was connected at the time of the last IO operation */
89                 internal bool is_connected;
90
91                 int m_IntCleanedUp;
92                 internal bool connect_in_progress;
93
94 #region Constructors
95
96
97                 public Socket (SocketInformation socketInformation)
98                 {
99                         this.is_listening      = (socketInformation.Options & SocketInformationOptions.Listening) != 0;
100                         this.is_connected      = (socketInformation.Options & SocketInformationOptions.Connected) != 0;
101                         this.is_blocking       = (socketInformation.Options & SocketInformationOptions.NonBlocking) == 0;
102                         this.useOverlappedIO = (socketInformation.Options & SocketInformationOptions.UseOnlyOverlappedIO) != 0;
103
104                         var result = Mono.DataConverter.Unpack ("iiiil", socketInformation.ProtocolInformation, 0);
105
106                         this.addressFamily = (AddressFamily) (int) result [0];
107                         this.socketType = (SocketType) (int) result [1];
108                         this.protocolType = (ProtocolType) (int) result [2];
109                         this.is_bound = (ProtocolType) (int) result [3] != 0;
110                         this.m_Handle = new SafeSocketHandle ((IntPtr) (long) result [4], true);
111
112                         InitializeSockets ();
113
114                         SocketDefaults ();
115                 }
116
117                 /* private constructor used by Accept, which already has a socket handle to use */
118                 internal Socket(AddressFamily family, SocketType type, ProtocolType proto, SafeSocketHandle safe_handle)
119                 {
120                         this.addressFamily = family;
121                         this.socketType = type;
122                         this.protocolType = proto;
123                         
124                         this.m_Handle = safe_handle;
125                         this.is_connected = true;
126
127                         InitializeSockets ();   
128                 }
129
130                 void SocketDefaults ()
131                 {
132                         try {
133                                 /* Need to test IPv6 further */
134                                 if (addressFamily == AddressFamily.InterNetwork
135                                         // || addressFamily == AddressFamily.InterNetworkV6
136                                 ) {
137                                         /* This is the default, but it probably has nasty side
138                                          * effects on Linux, as the socket option is kludged by
139                                          * turning on or off PMTU discovery... */
140                                         this.DontFragment = false;
141                                         if (protocolType == ProtocolType.Tcp)
142                                                 this.NoDelay = false;
143                                 } else if (addressFamily == AddressFamily.InterNetworkV6) {
144                                         this.DualMode = true;
145                                 }
146
147                                 /* Microsoft sets these to 8192, but we are going to keep them
148                                  * both to the OS defaults as these have a big performance impact.
149                                  * on WebClient performance. */
150                                 // this.ReceiveBufferSize = 8192;
151                                 // this.SendBufferSize = 8192;
152                         } catch (SocketException) {
153                         }
154                 }
155
156                 /* Creates a new system socket, returning the handle */
157                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
158                 extern IntPtr Socket_internal (AddressFamily family, SocketType type, ProtocolType proto, out int error);
159
160 #endregion
161
162 #region Properties
163
164                 public int Available {
165                         get {
166                                 ThrowIfDisposedAndClosed ();
167
168                                 int ret, error;
169                                 ret = Available_internal (m_Handle, out error);
170
171                                 if (error != 0)
172                                         throw new SocketException (error);
173
174                                 return ret;
175                         }
176                 }
177
178                 static int Available_internal (SafeSocketHandle safeHandle, out int error)
179                 {
180                         bool release = false;
181                         try {
182                                 safeHandle.DangerousAddRef (ref release);
183                                 return Available_internal (safeHandle.DangerousGetHandle (), out error);
184                         } finally {
185                                 if (release)
186                                         safeHandle.DangerousRelease ();
187                         }
188                 }
189
190                 /* Returns the amount of data waiting to be read on socket */
191                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
192                 extern static int Available_internal (IntPtr socket, out int error);
193
194                 // FIXME: import from referencesource
195                 public bool EnableBroadcast {
196                         get {
197                                 ThrowIfDisposedAndClosed ();
198
199                                 if (protocolType != ProtocolType.Udp)
200                                         throw new SocketException ((int) SocketError.ProtocolOption);
201
202                                 return ((int) GetSocketOption (SocketOptionLevel.Socket, SocketOptionName.Broadcast)) != 0;
203                         }
204                         set {
205                                 ThrowIfDisposedAndClosed ();
206
207                                 if (protocolType != ProtocolType.Udp)
208                                         throw new SocketException ((int) SocketError.ProtocolOption);
209
210                                 SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.Broadcast, value ? 1 : 0);
211                         }
212                 }
213
214                 public bool IsBound {
215                         get {
216                                 return is_bound;
217                         }
218                 }
219
220                 // FIXME: import from referencesource
221                 public bool MulticastLoopback {
222                         get {
223                                 ThrowIfDisposedAndClosed ();
224
225                                 /* Even though this option can be set for TCP sockets on Linux, throw
226                                  * this exception anyway to be compatible (the MSDN docs say
227                                  * "Setting this property on a Transmission Control Protocol (TCP)
228                                  * socket will have no effect." but the MS runtime throws the
229                                  * exception...) */
230                                 if (protocolType == ProtocolType.Tcp)
231                                         throw new SocketException ((int)SocketError.ProtocolOption);
232
233                                 switch (addressFamily) {
234                                 case AddressFamily.InterNetwork:
235                                         return ((int) GetSocketOption (SocketOptionLevel.IP, SocketOptionName.MulticastLoopback)) != 0;
236                                 case AddressFamily.InterNetworkV6:
237                                         return ((int) GetSocketOption (SocketOptionLevel.IPv6, SocketOptionName.MulticastLoopback)) != 0;
238                                 default:
239                                         throw new NotSupportedException ("This property is only valid for InterNetwork and InterNetworkV6 sockets");
240                                 }
241                         }
242                         set {
243                                 ThrowIfDisposedAndClosed ();
244
245                                 /* Even though this option can be set for TCP sockets on Linux, throw
246                                  * this exception anyway to be compatible (the MSDN docs say
247                                  * "Setting this property on a Transmission Control Protocol (TCP)
248                                  * socket will have no effect." but the MS runtime throws the
249                                  * exception...) */
250                                 if (protocolType == ProtocolType.Tcp)
251                                         throw new SocketException ((int)SocketError.ProtocolOption);
252
253                                 switch (addressFamily) {
254                                 case AddressFamily.InterNetwork:
255                                         SetSocketOption (SocketOptionLevel.IP, SocketOptionName.MulticastLoopback, value ? 1 : 0);
256                                         break;
257                                 case AddressFamily.InterNetworkV6:
258                                         SetSocketOption (SocketOptionLevel.IPv6, SocketOptionName.MulticastLoopback, value ? 1 : 0);
259                                         break;
260                                 default:
261                                         throw new NotSupportedException ("This property is only valid for InterNetwork and InterNetworkV6 sockets");
262                                 }
263                         }
264                 }
265
266                 // Wish:  support non-IP endpoints.
267                 public EndPoint LocalEndPoint {
268                         get {
269                                 ThrowIfDisposedAndClosed ();
270
271                                 /* If the seed EndPoint is null, Connect, Bind, etc has not yet
272                                  * been called. MS returns null in this case. */
273                                 if (seed_endpoint == null)
274                                         return null;
275
276                                 int error;
277                                 SocketAddress sa = LocalEndPoint_internal (m_Handle, (int) addressFamily, out error);
278
279                                 if (error != 0)
280                                         throw new SocketException (error);
281
282                                 return seed_endpoint.Create (sa);
283                         }
284                 }
285
286                 static SocketAddress LocalEndPoint_internal (SafeSocketHandle safeHandle, int family, out int error)
287                 {
288                         bool release = false;
289                         try {
290                                 safeHandle.DangerousAddRef (ref release);
291                                 return LocalEndPoint_internal (safeHandle.DangerousGetHandle (), family, out error);
292                         } finally {
293                                 if (release)
294                                         safeHandle.DangerousRelease ();
295                         }
296                 }
297
298                 /* Returns the local endpoint details in addr and port */
299                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
300                 extern static SocketAddress LocalEndPoint_internal (IntPtr socket, int family, out int error);
301
302                 public bool Blocking {
303                         get { return is_blocking; }
304                         set {
305                                 ThrowIfDisposedAndClosed ();
306
307                                 int error;
308                                 Blocking_internal (m_Handle, value, out error);
309
310                                 if (error != 0)
311                                         throw new SocketException (error);
312
313                                 is_blocking = value;
314                         }
315                 }
316
317                 static void Blocking_internal (SafeSocketHandle safeHandle, bool block, out int error)
318                 {
319                         bool release = false;
320                         try {
321                                 safeHandle.DangerousAddRef (ref release);
322                                 Blocking_internal (safeHandle.DangerousGetHandle (), block, out error);
323                         } finally {
324                                 if (release)
325                                         safeHandle.DangerousRelease ();
326                         }
327                 }
328
329                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
330                 internal extern static void Blocking_internal(IntPtr socket, bool block, out int error);
331
332                 public bool Connected {
333                         get { return is_connected; }
334                         internal set { is_connected = value; }
335                 }
336
337                 // FIXME: import from referencesource
338                 public bool NoDelay {
339                         get {
340                                 ThrowIfDisposedAndClosed ();
341                                 ThrowIfUdp ();
342
343                                 return ((int) GetSocketOption (SocketOptionLevel.Tcp, SocketOptionName.NoDelay)) != 0;
344                         }
345
346                         set {
347                                 ThrowIfDisposedAndClosed ();
348                                 ThrowIfUdp ();
349                                 SetSocketOption (SocketOptionLevel.Tcp, SocketOptionName.NoDelay, value ? 1 : 0);
350                         }
351                 }
352
353                 public EndPoint RemoteEndPoint {
354                         get {
355                                 ThrowIfDisposedAndClosed ();
356
357                                 /* If the seed EndPoint is null, Connect, Bind, etc has
358                                  * not yet been called. MS returns null in this case. */
359                                 if (!is_connected || seed_endpoint == null)
360                                         return null;
361
362                                 int error;
363                                 SocketAddress sa = RemoteEndPoint_internal (m_Handle, (int) addressFamily, out error);
364
365                                 if (error != 0)
366                                         throw new SocketException (error);
367
368                                 return seed_endpoint.Create (sa);
369                         }
370                 }
371
372                 static SocketAddress RemoteEndPoint_internal (SafeSocketHandle safeHandle, int family, out int error)
373                 {
374                         bool release = false;
375                         try {
376                                 safeHandle.DangerousAddRef (ref release);
377                                 return RemoteEndPoint_internal (safeHandle.DangerousGetHandle (), family, out error);
378                         } finally {
379                                 if (release)
380                                         safeHandle.DangerousRelease ();
381                         }
382                 }
383
384                 /* Returns the remote endpoint details in addr and port */
385                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
386                 extern static SocketAddress RemoteEndPoint_internal (IntPtr socket, int family, out int error);
387
388 #endregion
389
390 #region Select
391
392                 public static void Select (IList checkRead, IList checkWrite, IList checkError, int microSeconds)
393                 {
394                         var list = new List<Socket> ();
395                         AddSockets (list, checkRead, "checkRead");
396                         AddSockets (list, checkWrite, "checkWrite");
397                         AddSockets (list, checkError, "checkError");
398
399                         if (list.Count == 3)
400                                 throw new ArgumentNullException ("checkRead, checkWrite, checkError", "All the lists are null or empty.");
401
402                         /* The 'sockets' array contains:
403                          *  - READ socket 0-n, null,
404                          *  - WRITE socket 0-n, null,
405                          *  - ERROR socket 0-n, null */
406                         Socket [] sockets = list.ToArray ();
407
408                         int error;
409                         Select_internal (ref sockets, microSeconds, out error);
410
411                         if (error != 0)
412                                 throw new SocketException (error);
413
414                         if (sockets == null) {
415                                 if (checkRead != null)
416                                         checkRead.Clear ();
417                                 if (checkWrite != null)
418                                         checkWrite.Clear ();
419                                 if (checkError != null)
420                                         checkError.Clear ();
421                                 return;
422                         }
423
424                         int mode = 0;
425                         int count = sockets.Length;
426                         IList currentList = checkRead;
427                         int currentIdx = 0;
428                         for (int i = 0; i < count; i++) {
429                                 Socket sock = sockets [i];
430                                 if (sock == null) { // separator
431                                         if (currentList != null) {
432                                                 // Remove non-signaled sockets after the current one
433                                                 int to_remove = currentList.Count - currentIdx;
434                                                 for (int k = 0; k < to_remove; k++)
435                                                         currentList.RemoveAt (currentIdx);
436                                         }
437                                         currentList = (mode == 0) ? checkWrite : checkError;
438                                         currentIdx = 0;
439                                         mode++;
440                                         continue;
441                                 }
442
443                                 if (mode == 1 && currentList == checkWrite && !sock.is_connected) {
444                                         if ((int) sock.GetSocketOption (SocketOptionLevel.Socket, SocketOptionName.Error) == 0)
445                                                 sock.is_connected = true;
446                                 }
447
448                                 /* Remove non-signaled sockets before the current one */
449                                 while (((Socket) currentList [currentIdx]) != sock)
450                                         currentList.RemoveAt (currentIdx);
451
452                                 currentIdx++;
453                         }
454                 }
455
456                 static void AddSockets (List<Socket> sockets, IList list, string name)
457                 {
458                         if (list != null) {
459                                 foreach (Socket sock in list) {
460                                         if (sock == null) // MS throws a NullRef
461                                                 throw new ArgumentNullException (name, "Contains a null element");
462                                         sockets.Add (sock);
463                                 }
464                         }
465
466                         sockets.Add (null);
467                 }
468
469                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
470                 extern static void Select_internal (ref Socket [] sockets, int microSeconds, out int error);
471
472 #endregion
473
474 #region Poll
475
476                 public bool Poll (int microSeconds, SelectMode mode)
477                 {
478                         ThrowIfDisposedAndClosed ();
479
480                         if (mode != SelectMode.SelectRead && mode != SelectMode.SelectWrite && mode != SelectMode.SelectError)
481                                 throw new NotSupportedException ("'mode' parameter is not valid.");
482
483                         int error;
484                         bool result = Poll_internal (m_Handle, mode, microSeconds, out error);
485
486                         if (error != 0)
487                                 throw new SocketException (error);
488
489                         if (mode == SelectMode.SelectWrite && result && !is_connected) {
490                                 /* Update the is_connected state; for non-blocking Connect()
491                                  * this is when we can find out that the connect succeeded. */
492                                 if ((int) GetSocketOption (SocketOptionLevel.Socket, SocketOptionName.Error) == 0)
493                                         is_connected = true;
494                         }
495
496                         return result;
497                 }
498
499                 static bool Poll_internal (SafeSocketHandle safeHandle, SelectMode mode, int timeout, out int error)
500                 {
501                         bool release = false;
502                         try {
503                                 safeHandle.DangerousAddRef (ref release);
504                                 return Poll_internal (safeHandle.DangerousGetHandle (), mode, timeout, out error);
505                         } finally {
506                                 if (release)
507                                         safeHandle.DangerousRelease ();
508                         }
509                 }
510
511                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
512                 extern static bool Poll_internal (IntPtr socket, SelectMode mode, int timeout, out int error);
513
514 #endregion
515
516 #region Accept
517
518                 public Socket Accept()
519                 {
520                         ThrowIfDisposedAndClosed ();
521
522                         int error = 0;
523                         SafeSocketHandle safe_handle = Accept_internal (this.m_Handle, out error, is_blocking);
524
525                         if (error != 0) {
526                                 if (is_closed)
527                                         error = SOCKET_CLOSED_CODE;
528                                 throw new SocketException(error);
529                         }
530
531                         Socket accepted = new Socket (this.AddressFamily, this.SocketType, this.ProtocolType, safe_handle) {
532                                 seed_endpoint = this.seed_endpoint,
533                                 Blocking = this.Blocking,
534                         };
535
536                         return accepted;
537                 }
538
539                 internal void Accept (Socket acceptSocket)
540                 {
541                         ThrowIfDisposedAndClosed ();
542
543                         int error = 0;
544                         SafeSocketHandle safe_handle = Accept_internal (this.m_Handle, out error, is_blocking);
545
546                         if (error != 0) {
547                                 if (is_closed)
548                                         error = SOCKET_CLOSED_CODE;
549                                 throw new SocketException (error);
550                         }
551
552                         acceptSocket.addressFamily = this.AddressFamily;
553                         acceptSocket.socketType = this.SocketType;
554                         acceptSocket.protocolType = this.ProtocolType;
555                         acceptSocket.m_Handle = safe_handle;
556                         acceptSocket.is_connected = true;
557                         acceptSocket.seed_endpoint = this.seed_endpoint;
558                         acceptSocket.Blocking = this.Blocking;
559
560                         // FIXME: figure out what if anything else needs to be reset
561                 }
562
563                 public bool AcceptAsync (SocketAsyncEventArgs e)
564                 {
565                         // NO check is made whether e != null in MS.NET (NRE is thrown in such case)
566
567                         ThrowIfDisposedAndClosed ();
568
569                         if (!is_bound)
570                                 throw new InvalidOperationException ("You must call the Bind method before performing this operation.");
571                         if (!is_listening)
572                                 throw new InvalidOperationException ("You must call the Listen method before performing this operation.");
573                         if (e.BufferList != null)
574                                 throw new ArgumentException ("Multiple buffers cannot be used with this method.");
575                         if (e.Count < 0)
576                                 throw new ArgumentOutOfRangeException ("e.Count");
577
578                         Socket acceptSocket = e.AcceptSocket;
579                         if (acceptSocket != null) {
580                                 if (acceptSocket.is_bound || acceptSocket.is_connected)
581                                         throw new InvalidOperationException ("AcceptSocket: The socket must not be bound or connected.");
582                         }
583
584                         InitSocketAsyncEventArgs (e, AcceptAsyncCallback, e, SocketOperation.Accept);
585
586                         QueueIOSelectorJob (ReadSem, e.socket_async_result.Handle, new IOSelectorJob (IOOperation.Read, BeginAcceptCallback, e.socket_async_result));
587
588                         return true;
589                 }
590
591                 static AsyncCallback AcceptAsyncCallback = new AsyncCallback (ares => {
592                         SocketAsyncEventArgs e = (SocketAsyncEventArgs) ((SocketAsyncResult) ares).AsyncState;
593
594                         if (Interlocked.Exchange (ref e.in_progress, 0) != 1)
595                                 throw new InvalidOperationException ("No operation in progress");
596
597                         try {
598                                 e.AcceptSocket = e.current_socket.EndAccept (ares);
599                         } catch (SocketException ex) {
600                                 e.SocketError = ex.SocketErrorCode;
601                         } catch (ObjectDisposedException) {
602                                 e.SocketError = SocketError.OperationAborted;
603                         } finally {
604                                 if (e.AcceptSocket == null)
605                                         e.AcceptSocket = new Socket (e.current_socket.AddressFamily, e.current_socket.SocketType, e.current_socket.ProtocolType, null);
606                                 e.Complete ();
607                         }
608                 });
609
610                 public IAsyncResult BeginAccept(AsyncCallback callback, object state)
611                 {
612                         ThrowIfDisposedAndClosed ();
613
614                         if (!is_bound || !is_listening)
615                                 throw new InvalidOperationException ();
616
617                         SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.Accept);
618
619                         QueueIOSelectorJob (ReadSem, sockares.Handle, new IOSelectorJob (IOOperation.Read, BeginAcceptCallback, sockares));
620
621                         return sockares;
622                 }
623
624                 static IOAsyncCallback BeginAcceptCallback = new IOAsyncCallback (ares => {
625                         SocketAsyncResult sockares = (SocketAsyncResult) ares;
626                         Socket acc_socket = null;
627                         try {
628                                 if (sockares.AcceptSocket == null) {
629                                         acc_socket = sockares.socket.Accept ();
630                                 } else {
631                                         acc_socket = sockares.AcceptSocket;
632                                         sockares.socket.Accept (acc_socket);
633                                 }
634
635                         } catch (Exception e) {
636                                 sockares.Complete (e);
637                                 return;
638                         }
639                         sockares.Complete (acc_socket);
640                 });
641
642                 public IAsyncResult BeginAccept (Socket acceptSocket, int receiveSize, AsyncCallback callback, object state)
643                 {
644                         ThrowIfDisposedAndClosed ();
645
646                         if (receiveSize < 0)
647                                 throw new ArgumentOutOfRangeException ("receiveSize", "receiveSize is less than zero");
648
649                         if (acceptSocket != null) {
650                                 ThrowIfDisposedAndClosed (acceptSocket);
651
652                                 if (acceptSocket.IsBound)
653                                         throw new InvalidOperationException ();
654
655                                 /* For some reason the MS runtime
656                                  * barfs if the new socket is not TCP,
657                                  * even though it's just about to blow
658                                  * away all those parameters
659                                  */
660                                 if (acceptSocket.ProtocolType != ProtocolType.Tcp)
661                                         throw new SocketException ((int)SocketError.InvalidArgument);
662                         }
663
664                         SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.AcceptReceive) {
665                                 Buffer = new byte [receiveSize],
666                                 Offset = 0,
667                                 Size = receiveSize,
668                                 SockFlags = SocketFlags.None,
669                                 AcceptSocket = acceptSocket,
670                         };
671
672                         QueueIOSelectorJob (ReadSem, sockares.Handle, new IOSelectorJob (IOOperation.Read, BeginAcceptReceiveCallback, sockares));
673
674                         return sockares;
675                 }
676
677                 static IOAsyncCallback BeginAcceptReceiveCallback = new IOAsyncCallback (ares => {
678                         SocketAsyncResult sockares = (SocketAsyncResult) ares;
679                         Socket acc_socket = null;
680
681                         try {
682                                 if (sockares.AcceptSocket == null) {
683                                         acc_socket = sockares.socket.Accept ();
684                                 } else {
685                                         acc_socket = sockares.AcceptSocket;
686                                         sockares.socket.Accept (acc_socket);
687                                 }
688                         } catch (Exception e) {
689                                 sockares.Complete (e);
690                                 return;
691                         }
692
693                         /* It seems the MS runtime special-cases 0-length requested receive data.  See bug 464201. */
694                         int total = 0;
695                         if (sockares.Size > 0) {
696                                 try {
697                                         SocketError error;
698                                         total = acc_socket.Receive (sockares.Buffer, sockares.Offset, sockares.Size, sockares.SockFlags, out error);
699                                         if (error != 0) {
700                                                 sockares.Complete (new SocketException ((int) error));
701                                                 return;
702                                         }
703                                 } catch (Exception e) {
704                                         sockares.Complete (e);
705                                         return;
706                                 }
707                         }
708
709                         sockares.Complete (acc_socket, total);
710                 });
711
712                 public Socket EndAccept (IAsyncResult asyncResult)
713                 {
714                         int bytes;
715                         byte[] buffer;
716                         return EndAccept (out buffer, out bytes, asyncResult);
717                 }
718
719                 public Socket EndAccept (out byte[] buffer, out int bytesTransferred, IAsyncResult asyncResult)
720                 {
721                         ThrowIfDisposedAndClosed ();
722
723                         SocketAsyncResult sockares = ValidateEndIAsyncResult (asyncResult, "EndAccept", "asyncResult");
724
725                         if (!sockares.IsCompleted)
726                                 sockares.AsyncWaitHandle.WaitOne ();
727
728                         sockares.CheckIfThrowDelayedException ();
729
730                         buffer = sockares.Buffer;
731                         bytesTransferred = sockares.Total;
732
733                         return sockares.AcceptedSocket;
734                 }
735
736                 static SafeSocketHandle Accept_internal (SafeSocketHandle safeHandle, out int error, bool blocking)
737                 {
738                         try {
739                                 safeHandle.RegisterForBlockingSyscall ();
740                                 var ret = Accept_internal (safeHandle.DangerousGetHandle (), out error, blocking);
741                                 return new SafeSocketHandle (ret, true);
742                         } finally {
743                                 safeHandle.UnRegisterForBlockingSyscall ();
744                         }
745                 }
746
747                 /* Creates a new system socket, returning the handle */
748                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
749                 extern static IntPtr Accept_internal (IntPtr sock, out int error, bool blocking);
750
751 #endregion
752
753 #region Bind
754
755                 public void Bind (EndPoint localEP)
756                 {
757 #if FEATURE_NO_BSD_SOCKETS
758                         throw new PlatformNotSupportedException ("System.Net.Sockets.Socket:Bind is not supported on this platform.");
759 #else
760                         ThrowIfDisposedAndClosed ();
761
762                         if (localEP == null)
763                                 throw new ArgumentNullException("localEP");
764                                 
765                         var ipEndPoint = localEP as IPEndPoint;
766                         if (ipEndPoint != null) {
767                                 localEP = RemapIPEndPoint (ipEndPoint); 
768                         }
769                         
770                         int error;
771                         Bind_internal (m_Handle, localEP.Serialize(), out error);
772
773                         if (error != 0)
774                                 throw new SocketException (error);
775                         if (error == 0)
776                                 is_bound = true;
777
778                         seed_endpoint = localEP;
779 #endif // FEATURE_NO_BSD_SOCKETS
780                 }
781
782                 private static void Bind_internal (SafeSocketHandle safeHandle, SocketAddress sa, out int error)
783                 {
784                         bool release = false;
785                         try {
786                                 safeHandle.DangerousAddRef (ref release);
787                                 Bind_internal (safeHandle.DangerousGetHandle (), sa, out error);
788                         } finally {
789                                 if (release)
790                                         safeHandle.DangerousRelease ();
791                         }
792                 }
793
794                 // Creates a new system socket, returning the handle
795                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
796                 private extern static void Bind_internal(IntPtr sock, SocketAddress sa, out int error);
797
798 #endregion
799
800 #region Listen
801
802                 public void Listen (int backlog)
803                 {
804                         ThrowIfDisposedAndClosed ();
805
806                         if (!is_bound)
807                                 throw new SocketException ((int) SocketError.InvalidArgument);
808
809                         int error;
810                         Listen_internal(m_Handle, backlog, out error);
811
812                         if (error != 0)
813                                 throw new SocketException (error);
814
815                         is_listening = true;
816                 }
817
818                 static void Listen_internal (SafeSocketHandle safeHandle, int backlog, out int error)
819                 {
820                         bool release = false;
821                         try {
822                                 safeHandle.DangerousAddRef (ref release);
823                                 Listen_internal (safeHandle.DangerousGetHandle (), backlog, out error);
824                         } finally {
825                                 if (release)
826                                         safeHandle.DangerousRelease ();
827                         }
828                 }
829
830                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
831                 extern static void Listen_internal (IntPtr sock, int backlog, out int error);
832
833 #endregion
834
835 #region Connect
836
837                 public void Connect (IPAddress address, int port)
838                 {
839                         Connect (new IPEndPoint (address, port));
840                 }
841
842                 public void Connect (string host, int port)
843                 {
844                         Connect (Dns.GetHostAddresses (host), port);
845                 }
846
847                 public void Connect (EndPoint remoteEP)
848                 {
849                         ThrowIfDisposedAndClosed ();
850
851                         if (remoteEP == null)
852                                 throw new ArgumentNullException ("remoteEP");
853
854                         IPEndPoint ep = remoteEP as IPEndPoint;
855                         /* Dgram uses Any to 'disconnect' */
856                         if (ep != null && socketType != SocketType.Dgram) {
857                                 if (ep.Address.Equals (IPAddress.Any) || ep.Address.Equals (IPAddress.IPv6Any))
858                                         throw new SocketException ((int) SocketError.AddressNotAvailable);
859                         }
860
861                         if (is_listening)
862                                 throw new InvalidOperationException ();
863                                 
864                         if (ep != null) {
865                                 remoteEP = RemapIPEndPoint (ep);
866                         }
867
868                         SocketAddress serial = remoteEP.Serialize ();
869
870                         int error = 0;
871                         Connect_internal (m_Handle, serial, out error, is_blocking);
872
873                         if (error == 0 || error == 10035)
874                                 seed_endpoint = remoteEP; // Keep the ep around for non-blocking sockets
875
876                         if (error != 0) {
877                                 if (is_closed)
878                                         error = SOCKET_CLOSED_CODE;
879                                 throw new SocketException (error);
880                         }
881
882                         is_connected = !(socketType == SocketType.Dgram && ep != null && (ep.Address.Equals (IPAddress.Any) || ep.Address.Equals (IPAddress.IPv6Any)));
883                         is_bound = true;
884                 }
885
886                 public bool ConnectAsync (SocketAsyncEventArgs e)
887                 {
888                         // NO check is made whether e != null in MS.NET (NRE is thrown in such case)
889
890                         ThrowIfDisposedAndClosed ();
891
892                         if (is_listening)
893                                 throw new InvalidOperationException ("You may not perform this operation after calling the Listen method.");
894                         if (e.RemoteEndPoint == null)
895                                 throw new ArgumentNullException ("remoteEP");
896
897                         InitSocketAsyncEventArgs (e, null, e, SocketOperation.Connect);
898
899                         try {
900                                 IPAddress [] addresses;
901                                 SocketAsyncResult ares;
902
903                                 if (!GetCheckedIPs (e, out addresses)) {
904                                         //NOTE: DualMode may cause Socket's RemoteEndpoint to differ in AddressFamily from the
905                                         // SocketAsyncEventArgs, but the SocketAsyncEventArgs itself is not changed
906                                         ares = (SocketAsyncResult) BeginConnect (e.RemoteEndPoint, ConnectAsyncCallback, e);
907                                 } else {
908                                         DnsEndPoint dep = (DnsEndPoint)e.RemoteEndPoint;
909                                         ares = (SocketAsyncResult) BeginConnect (addresses, dep.Port, ConnectAsyncCallback, e);
910                                 }
911
912                                 if (ares.IsCompleted && ares.CompletedSynchronously) {
913                                         ares.CheckIfThrowDelayedException ();
914                                         return false;
915                                 }
916                         } catch (Exception exc) {
917                                 e.socket_async_result.Complete (exc, true);
918                                 return false;
919                         }
920
921                         return true;
922                 }
923
924                 public static void CancelConnectAsync (SocketAsyncEventArgs e)
925                 {
926                         if (e == null)
927                                 throw new ArgumentNullException("e");
928
929                         if (e.in_progress != 0 && e.LastOperation == SocketAsyncOperation.Connect)
930                                 e.current_socket.Close();
931                 }
932
933                 static AsyncCallback ConnectAsyncCallback = new AsyncCallback (ares => {
934                         SocketAsyncEventArgs e = (SocketAsyncEventArgs) ((SocketAsyncResult) ares).AsyncState;
935
936                         if (Interlocked.Exchange (ref e.in_progress, 0) != 1)
937                                 throw new InvalidOperationException ("No operation in progress");
938
939                         try {
940                                 e.current_socket.EndConnect (ares);
941                         } catch (SocketException se) {
942                                 e.SocketError = se.SocketErrorCode;
943                         } catch (ObjectDisposedException) {
944                                 e.SocketError = SocketError.OperationAborted;
945                         } finally {
946                                 e.Complete ();
947                         }
948                 });
949
950                 public IAsyncResult BeginConnect (string host, int port, AsyncCallback requestCallback, object state)
951                 {
952                         ThrowIfDisposedAndClosed ();
953
954                         if (host == null)
955                                 throw new ArgumentNullException ("host");
956                         if (addressFamily != AddressFamily.InterNetwork && addressFamily != AddressFamily.InterNetworkV6)
957                                 throw new NotSupportedException ("This method is valid only for sockets in the InterNetwork and InterNetworkV6 families");
958                         if (port <= 0 || port > 65535)
959                                 throw new ArgumentOutOfRangeException ("port", "Must be > 0 and < 65536");
960                         if (is_listening)
961                                 throw new InvalidOperationException ();
962
963                         return BeginConnect (Dns.GetHostAddresses (host), port, requestCallback, state);
964                 }
965
966                 public IAsyncResult BeginConnect (EndPoint remoteEP, AsyncCallback callback, object state)
967                 {
968                         ThrowIfDisposedAndClosed ();
969
970                         if (remoteEP == null)
971                                 throw new ArgumentNullException ("remoteEP");
972                         if (is_listening)
973                                 throw new InvalidOperationException ();
974
975                         SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.Connect) {
976                                 EndPoint = remoteEP,
977                         };
978
979                         BeginSConnect (sockares);
980                         return sockares;
981                 }
982
983                 public IAsyncResult BeginConnect (IPAddress[] addresses, int port, AsyncCallback requestCallback, object state)
984                 {
985                         ThrowIfDisposedAndClosed ();
986
987                         if (addresses == null)
988                                 throw new ArgumentNullException ("addresses");
989                         if (addresses.Length == 0)
990                                 throw new ArgumentException ("Empty addresses list");
991                         if (this.AddressFamily != AddressFamily.InterNetwork && this.AddressFamily != AddressFamily.InterNetworkV6)
992                                 throw new NotSupportedException ("This method is only valid for addresses in the InterNetwork or InterNetworkV6 families");
993                         if (port <= 0 || port > 65535)
994                                 throw new ArgumentOutOfRangeException ("port", "Must be > 0 and < 65536");
995                         if (is_listening)
996                                 throw new InvalidOperationException ();
997
998                         SocketAsyncResult sockares = new SocketAsyncResult (this, requestCallback, state, SocketOperation.Connect) {
999                                 Addresses = addresses,
1000                                 Port = port,
1001                         };
1002
1003                         is_connected = false;
1004
1005                         BeginMConnect (sockares);
1006                         return sockares;
1007                 }
1008
1009                 static void BeginMConnect (SocketAsyncResult sockares)
1010                 {
1011                         Exception exc = null;
1012
1013                         for (int i = sockares.CurrentAddress; i < sockares.Addresses.Length; i++) {
1014                                 try {
1015                                         sockares.CurrentAddress++;
1016                                         sockares.EndPoint = new IPEndPoint (sockares.Addresses [i], sockares.Port);
1017
1018                                         BeginSConnect (sockares);
1019                                         return;
1020                                 } catch (Exception e) {
1021                                         exc = e;
1022                                 }
1023                         }
1024
1025                         throw exc;
1026                 }
1027
1028                 static void BeginSConnect (SocketAsyncResult sockares)
1029                 {
1030                         EndPoint remoteEP = sockares.EndPoint;
1031                         // Bug #75154: Connect() should not succeed for .Any addresses.
1032                         if (remoteEP is IPEndPoint) {
1033                                 IPEndPoint ep = (IPEndPoint) remoteEP;
1034                                 if (ep.Address.Equals (IPAddress.Any) || ep.Address.Equals (IPAddress.IPv6Any)) {
1035                                         sockares.Complete (new SocketException ((int) SocketError.AddressNotAvailable), true);
1036                                         return;
1037                                 }
1038
1039                                 sockares.EndPoint = remoteEP = sockares.socket.RemapIPEndPoint (ep);
1040                         }
1041
1042                         int error = 0;
1043
1044                         if (sockares.socket.connect_in_progress) {
1045                                 // This could happen when multiple IPs are used
1046                                 // Calling connect() again will reset the connection attempt and cause
1047                                 // an error. Better to just close the socket and move on.
1048                                 sockares.socket.connect_in_progress = false;
1049                                 sockares.socket.m_Handle.Dispose ();
1050                                 sockares.socket.m_Handle = new SafeSocketHandle (sockares.socket.Socket_internal (sockares.socket.addressFamily, sockares.socket.socketType, sockares.socket.protocolType, out error), true);
1051                                 if (error != 0)
1052                                         throw new SocketException (error);
1053                         }
1054
1055                         bool blk = sockares.socket.is_blocking;
1056                         if (blk)
1057                                 sockares.socket.Blocking = false;
1058                         Connect_internal (sockares.socket.m_Handle, remoteEP.Serialize (), out error, false);
1059                         if (blk)
1060                                 sockares.socket.Blocking = true;
1061
1062                         if (error == 0) {
1063                                 // succeeded synch
1064                                 sockares.socket.is_connected = true;
1065                                 sockares.socket.is_bound = true;
1066                                 sockares.Complete (true);
1067                                 return;
1068                         }
1069
1070                         if (error != (int) SocketError.InProgress && error != (int) SocketError.WouldBlock) {
1071                                 // error synch
1072                                 sockares.socket.is_connected = false;
1073                                 sockares.socket.is_bound = false;
1074                                 sockares.Complete (new SocketException (error), true);
1075                                 return;
1076                         }
1077
1078                         // continue asynch
1079                         sockares.socket.is_connected = false;
1080                         sockares.socket.is_bound = false;
1081                         sockares.socket.connect_in_progress = true;
1082
1083                         IOSelector.Add (sockares.Handle, new IOSelectorJob (IOOperation.Write, BeginConnectCallback, sockares));
1084                 }
1085
1086                 static IOAsyncCallback BeginConnectCallback = new IOAsyncCallback (ares => {
1087                         SocketAsyncResult sockares = (SocketAsyncResult) ares;
1088
1089                         if (sockares.EndPoint == null) {
1090                                 sockares.Complete (new SocketException ((int)SocketError.AddressNotAvailable));
1091                                 return;
1092                         }
1093
1094                         try {
1095                                 int error = (int) sockares.socket.GetSocketOption (SocketOptionLevel.Socket, SocketOptionName.Error);
1096
1097                                 if (error == 0) {
1098                                         sockares.socket.seed_endpoint = sockares.EndPoint;
1099                                         sockares.socket.is_connected = true;
1100                                         sockares.socket.is_bound = true;
1101                                         sockares.socket.connect_in_progress = false;
1102                                         sockares.error = 0;
1103                                         sockares.Complete ();
1104                                         return;
1105                                 }
1106
1107                                 if (sockares.Addresses == null) {
1108                                         sockares.socket.connect_in_progress = false;
1109                                         sockares.Complete (new SocketException (error));
1110                                         return;
1111                                 }
1112
1113                                 if (sockares.CurrentAddress >= sockares.Addresses.Length) {
1114                                         sockares.Complete (new SocketException (error));
1115                                         return;
1116                                 }
1117
1118                                 BeginMConnect (sockares);
1119                         } catch (Exception e) {
1120                                 sockares.socket.connect_in_progress = false;
1121                                 sockares.Complete (e);
1122                         }
1123                 });
1124
1125                 public void EndConnect (IAsyncResult asyncResult)
1126                 {
1127                         ThrowIfDisposedAndClosed ();
1128
1129                         SocketAsyncResult sockares = ValidateEndIAsyncResult (asyncResult, "EndConnect", "asyncResult");
1130
1131                         if (!sockares.IsCompleted)
1132                                 sockares.AsyncWaitHandle.WaitOne();
1133
1134                         sockares.CheckIfThrowDelayedException();
1135                 }
1136
1137                 static void Connect_internal (SafeSocketHandle safeHandle, SocketAddress sa, out int error, bool blocking)
1138                 {
1139                         try {
1140                                 safeHandle.RegisterForBlockingSyscall ();
1141                                 Connect_internal (safeHandle.DangerousGetHandle (), sa, out error, blocking);
1142                         } finally {
1143                                 safeHandle.UnRegisterForBlockingSyscall ();
1144                         }
1145                 }
1146
1147                 /* Connects to the remote address */
1148                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1149                 extern static void Connect_internal(IntPtr sock, SocketAddress sa, out int error, bool blocking);
1150
1151                 /* Returns :
1152                  *  - false when it is ok to use RemoteEndPoint
1153                  *  - true when addresses must be used (and addresses could be null/empty) */
1154                 bool GetCheckedIPs (SocketAsyncEventArgs e, out IPAddress [] addresses)
1155                 {
1156                         addresses = null;
1157
1158                         // Connect to the first address that match the host name, like:
1159                         // http://blogs.msdn.com/ncl/archive/2009/07/20/new-ncl-features-in-net-4-0-beta-2.aspx
1160                         // while skipping entries that do not match the address family
1161                         DnsEndPoint dep = e.RemoteEndPoint as DnsEndPoint;
1162                         if (dep != null) {
1163                                 addresses = Dns.GetHostAddresses (dep.Host);
1164                                 return true;
1165                         } else {
1166                                 e.ConnectByNameError = null;
1167                                 return false;
1168                         }
1169                 }
1170
1171 #endregion
1172
1173 #region Disconnect
1174
1175                 /* According to the docs, the MS runtime will throw PlatformNotSupportedException
1176                  * if the platform is newer than w2k.  We should be able to cope... */
1177                 public void Disconnect (bool reuseSocket)
1178                 {
1179                         ThrowIfDisposedAndClosed ();
1180
1181                         int error = 0;
1182                         Disconnect_internal (m_Handle, reuseSocket, out error);
1183
1184                         if (error != 0) {
1185                                 if (error == 50) {
1186                                         /* ERROR_NOT_SUPPORTED */
1187                                         throw new PlatformNotSupportedException ();
1188                                 } else {
1189                                         throw new SocketException (error);
1190                                 }
1191                         }
1192
1193                         is_connected = false;
1194                         if (reuseSocket) {
1195                                 /* Do managed housekeeping here... */
1196                         }
1197                 }
1198
1199                 public bool DisconnectAsync (SocketAsyncEventArgs e)
1200                 {
1201                         // NO check is made whether e != null in MS.NET (NRE is thrown in such case)
1202
1203                         ThrowIfDisposedAndClosed ();
1204
1205                         InitSocketAsyncEventArgs (e, DisconnectAsyncCallback, e, SocketOperation.Disconnect);
1206
1207                         IOSelector.Add (e.socket_async_result.Handle, new IOSelectorJob (IOOperation.Write, BeginDisconnectCallback, e.socket_async_result));
1208
1209                         return true;
1210                 }
1211
1212                 static AsyncCallback DisconnectAsyncCallback = new AsyncCallback (ares => {
1213                         SocketAsyncEventArgs e = (SocketAsyncEventArgs) ((SocketAsyncResult) ares).AsyncState;
1214
1215                         if (Interlocked.Exchange (ref e.in_progress, 0) != 1)
1216                                 throw new InvalidOperationException ("No operation in progress");
1217
1218                         try {
1219                                 e.current_socket.EndDisconnect (ares);
1220                         } catch (SocketException ex) {
1221                                 e.SocketError = ex.SocketErrorCode;
1222                         } catch (ObjectDisposedException) {
1223                                 e.SocketError = SocketError.OperationAborted;
1224                         } finally {
1225                                 e.Complete ();
1226                         }
1227                 });
1228
1229                 public IAsyncResult BeginDisconnect (bool reuseSocket, AsyncCallback callback, object state)
1230                 {
1231                         ThrowIfDisposedAndClosed ();
1232
1233                         SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.Disconnect) {
1234                                 ReuseSocket = reuseSocket,
1235                         };
1236
1237                         IOSelector.Add (sockares.Handle, new IOSelectorJob (IOOperation.Write, BeginDisconnectCallback, sockares));
1238
1239                         return sockares;
1240                 }
1241
1242                 static IOAsyncCallback BeginDisconnectCallback = new IOAsyncCallback (ares => {
1243                         SocketAsyncResult sockares = (SocketAsyncResult) ares;
1244
1245                         try {
1246                                 sockares.socket.Disconnect (sockares.ReuseSocket);
1247                         } catch (Exception e) {
1248                                 sockares.Complete (e);
1249                                 return;
1250                         }
1251
1252                         sockares.Complete ();
1253                 });
1254
1255                 public void EndDisconnect (IAsyncResult asyncResult)
1256                 {
1257                         ThrowIfDisposedAndClosed ();
1258
1259                         SocketAsyncResult sockares = ValidateEndIAsyncResult (asyncResult, "EndDisconnect", "asyncResult");
1260
1261                         if (!sockares.IsCompleted)
1262                                 sockares.AsyncWaitHandle.WaitOne ();
1263
1264                         sockares.CheckIfThrowDelayedException ();
1265                 }
1266
1267                 static void Disconnect_internal (SafeSocketHandle safeHandle, bool reuse, out int error)
1268                 {
1269                         bool release = false;
1270                         try {
1271                                 safeHandle.DangerousAddRef (ref release);
1272                                 Disconnect_internal (safeHandle.DangerousGetHandle (), reuse, out error);
1273                         } finally {
1274                                 if (release)
1275                                         safeHandle.DangerousRelease ();
1276                         }
1277                 }
1278
1279                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1280                 extern static void Disconnect_internal (IntPtr sock, bool reuse, out int error);
1281
1282 #endregion
1283
1284 #region Receive
1285
1286                 public int Receive (byte [] buffer, int offset, int size, SocketFlags socketFlags, out SocketError errorCode)
1287                 {
1288                         ThrowIfDisposedAndClosed ();
1289                         ThrowIfBufferNull (buffer);
1290                         ThrowIfBufferOutOfRange (buffer, offset, size);
1291
1292                         int nativeError;
1293                         int ret;
1294                         unsafe {
1295                                 fixed (byte* pbuffer = buffer) {
1296                                         ret = Receive_internal (m_Handle, &pbuffer[offset], size, socketFlags, out nativeError, is_blocking);
1297                                 }
1298                         }
1299
1300                         errorCode = (SocketError) nativeError;
1301                         if (errorCode != SocketError.Success && errorCode != SocketError.WouldBlock && errorCode != SocketError.InProgress) {
1302                                 is_connected = false;
1303                                 is_bound = false;
1304                         } else {
1305                                 is_connected = true;
1306                         }
1307
1308                         return ret;
1309                 }
1310
1311                 [CLSCompliant (false)]
1312                 public int Receive (IList<ArraySegment<byte>> buffers, SocketFlags socketFlags, out SocketError errorCode)
1313                 {
1314                         ThrowIfDisposedAndClosed ();
1315
1316                         if (buffers == null || buffers.Count == 0)
1317                                 throw new ArgumentNullException ("buffers");
1318
1319                         int numsegments = buffers.Count;
1320                         int nativeError;
1321                         int ret;
1322
1323                         GCHandle[] gch = new GCHandle[numsegments];
1324                         try {
1325                                 unsafe {
1326                                         fixed (WSABUF* bufarray = new WSABUF[numsegments]) {
1327                                                 for (int i = 0; i < numsegments; i++) {
1328                                                         ArraySegment<byte> segment = buffers[i];
1329
1330                                                         if (segment.Offset < 0 || segment.Count < 0 || segment.Count > segment.Array.Length - segment.Offset)
1331                                                                 throw new ArgumentOutOfRangeException ("segment");
1332
1333                                                         try {} finally {
1334                                                                 gch[i] = GCHandle.Alloc (segment.Array, GCHandleType.Pinned);
1335                                                         }
1336
1337                                                         bufarray[i].len = segment.Count;
1338                                                         bufarray[i].buf = Marshal.UnsafeAddrOfPinnedArrayElement (segment.Array, segment.Offset);
1339                                                 }
1340
1341                                                 ret = Receive_internal (m_Handle, bufarray, numsegments, socketFlags, out nativeError, is_blocking);
1342                                         }
1343                                 }
1344                         } finally {
1345                                 for (int i = 0; i < numsegments; i++) {
1346                                         if (gch[i].IsAllocated)
1347                                                 gch[i].Free ();
1348                                 }
1349                         }
1350
1351                         errorCode = (SocketError) nativeError;
1352
1353                         return ret;
1354                 }
1355
1356                 public bool ReceiveAsync (SocketAsyncEventArgs e)
1357                 {
1358                         // NO check is made whether e != null in MS.NET (NRE is thrown in such case)
1359
1360                         ThrowIfDisposedAndClosed ();
1361
1362                         // LAME SPEC: the ArgumentException is never thrown, instead an NRE is
1363                         // thrown when e.Buffer and e.BufferList are null (works fine when one is
1364                         // set to a valid object)
1365                         if (e.Buffer == null && e.BufferList == null)
1366                                 throw new NullReferenceException ("Either e.Buffer or e.BufferList must be valid buffers.");
1367
1368                         if (e.Buffer == null) {
1369                                 InitSocketAsyncEventArgs (e, ReceiveAsyncCallback, e, SocketOperation.ReceiveGeneric);
1370
1371                                 e.socket_async_result.Buffers = e.BufferList;
1372
1373                                 QueueIOSelectorJob (ReadSem, e.socket_async_result.Handle, new IOSelectorJob (IOOperation.Read, BeginReceiveGenericCallback, e.socket_async_result));
1374                         } else {
1375                                 InitSocketAsyncEventArgs (e, ReceiveAsyncCallback, e, SocketOperation.Receive);
1376
1377                                 e.socket_async_result.Buffer = e.Buffer;
1378                                 e.socket_async_result.Offset = e.Offset;
1379                                 e.socket_async_result.Size = e.Count;
1380
1381                                 QueueIOSelectorJob (ReadSem, e.socket_async_result.Handle, new IOSelectorJob (IOOperation.Read, BeginReceiveCallback, e.socket_async_result));
1382                         }
1383
1384                         return true;
1385                 }
1386
1387                 static AsyncCallback ReceiveAsyncCallback = new AsyncCallback (ares => {
1388                         SocketAsyncEventArgs e = (SocketAsyncEventArgs) ((SocketAsyncResult) ares).AsyncState;
1389
1390                         if (Interlocked.Exchange (ref e.in_progress, 0) != 1)
1391                                 throw new InvalidOperationException ("No operation in progress");
1392
1393                         try {
1394                                 e.BytesTransferred = e.current_socket.EndReceive (ares);
1395                         } catch (SocketException se){
1396                                 e.SocketError = se.SocketErrorCode;
1397                         } catch (ObjectDisposedException) {
1398                                 e.SocketError = SocketError.OperationAborted;
1399                         } finally {
1400                                 e.Complete ();
1401                         }
1402                 });
1403
1404                 public IAsyncResult BeginReceive (byte[] buffer, int offset, int size, SocketFlags socketFlags, out SocketError errorCode, AsyncCallback callback, object state)
1405                 {
1406                         ThrowIfDisposedAndClosed ();
1407                         ThrowIfBufferNull (buffer);
1408                         ThrowIfBufferOutOfRange (buffer, offset, size);
1409
1410                         /* As far as I can tell from the docs and from experimentation, a pointer to the
1411                          * SocketError parameter is not supposed to be saved for the async parts.  And as we don't
1412                          * set any socket errors in the setup code, we just have to set it to Success. */
1413                         errorCode = SocketError.Success;
1414
1415                         SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.Receive) {
1416                                 Buffer = buffer,
1417                                 Offset = offset,
1418                                 Size = size,
1419                                 SockFlags = socketFlags,
1420                         };
1421
1422                         QueueIOSelectorJob (ReadSem, sockares.Handle, new IOSelectorJob (IOOperation.Read, BeginReceiveCallback, sockares));
1423
1424                         return sockares;
1425                 }
1426
1427                 static IOAsyncCallback BeginReceiveCallback = new IOAsyncCallback (ares => {
1428                         SocketAsyncResult sockares = (SocketAsyncResult) ares;
1429                         int total = 0;
1430
1431                         try {
1432                                 unsafe {
1433                                         fixed (byte* pbuffer = sockares.Buffer) {
1434                                                 total = Receive_internal (sockares.socket.m_Handle, &pbuffer[sockares.Offset], sockares.Size, sockares.SockFlags, out sockares.error, sockares.socket.is_blocking);
1435                                         }
1436                                 }
1437                         } catch (Exception e) {
1438                                 sockares.Complete (e);
1439                                 return;
1440                         }
1441
1442                         sockares.Complete (total);
1443                 });
1444
1445                 [CLSCompliant (false)]
1446                 public IAsyncResult BeginReceive (IList<ArraySegment<byte>> buffers, SocketFlags socketFlags, out SocketError errorCode, AsyncCallback callback, object state)
1447                 {
1448                         ThrowIfDisposedAndClosed ();
1449
1450                         if (buffers == null)
1451                                 throw new ArgumentNullException ("buffers");
1452
1453                         /* I assume the same SocketError semantics as above */
1454                         errorCode = SocketError.Success;
1455
1456                         SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.ReceiveGeneric) {
1457                                 Buffers = buffers,
1458                                 SockFlags = socketFlags,
1459                         };
1460
1461                         QueueIOSelectorJob (ReadSem, sockares.Handle, new IOSelectorJob (IOOperation.Read, BeginReceiveGenericCallback, sockares));
1462
1463                         return sockares;
1464                 }
1465
1466                 static IOAsyncCallback BeginReceiveGenericCallback = new IOAsyncCallback (ares => {
1467                         SocketAsyncResult sockares = (SocketAsyncResult) ares;
1468                         int total = 0;
1469
1470                         try {
1471                                 total = sockares.socket.Receive (sockares.Buffers, sockares.SockFlags);
1472                         } catch (Exception e) {
1473                                 sockares.Complete (e);
1474                                 return;
1475                         }
1476
1477                         sockares.Complete (total);
1478                 });
1479
1480                 public int EndReceive (IAsyncResult asyncResult, out SocketError errorCode)
1481                 {
1482                         ThrowIfDisposedAndClosed ();
1483
1484                         SocketAsyncResult sockares = ValidateEndIAsyncResult (asyncResult, "EndReceive", "asyncResult");
1485
1486                         if (!sockares.IsCompleted)
1487                                 sockares.AsyncWaitHandle.WaitOne ();
1488
1489                         errorCode = sockares.ErrorCode;
1490
1491                         if (errorCode != SocketError.Success && errorCode != SocketError.WouldBlock && errorCode != SocketError.InProgress)
1492                                 is_connected = false;
1493
1494                         // If no socket error occurred, call CheckIfThrowDelayedException in case there are other
1495                         // kinds of exceptions that should be thrown.
1496                         if (errorCode == SocketError.Success)
1497                                 sockares.CheckIfThrowDelayedException();
1498
1499                         return sockares.Total;
1500                 }
1501
1502                 static unsafe int Receive_internal (SafeSocketHandle safeHandle, WSABUF* bufarray, int count, SocketFlags flags, out int error, bool blocking)
1503                 {
1504                         try {
1505                                 safeHandle.RegisterForBlockingSyscall ();
1506                                 return Receive_internal (safeHandle.DangerousGetHandle (), bufarray, count, flags, out error, blocking);
1507                         } finally {
1508                                 safeHandle.UnRegisterForBlockingSyscall ();
1509                         }
1510                 }
1511
1512                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1513                 extern static unsafe int Receive_internal (IntPtr sock, WSABUF* bufarray, int count, SocketFlags flags, out int error, bool blocking);
1514
1515                 static unsafe int Receive_internal (SafeSocketHandle safeHandle, byte* buffer, int count, SocketFlags flags, out int error, bool blocking)
1516                 {
1517                         try {
1518                                 safeHandle.RegisterForBlockingSyscall ();
1519                                 return Receive_internal (safeHandle.DangerousGetHandle (), buffer, count, flags, out error, blocking);
1520                         } finally {
1521                                 safeHandle.UnRegisterForBlockingSyscall ();
1522                         }
1523                 }
1524
1525                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1526                 extern static unsafe int Receive_internal(IntPtr sock, byte* buffer, int count, SocketFlags flags, out int error, bool blocking);
1527
1528 #endregion
1529
1530 #region ReceiveFrom
1531
1532                 public int ReceiveFrom (byte [] buffer, int offset, int size, SocketFlags socketFlags, ref EndPoint remoteEP)
1533                 {
1534                         ThrowIfDisposedAndClosed ();
1535                         ThrowIfBufferNull (buffer);
1536                         ThrowIfBufferOutOfRange (buffer, offset, size);
1537
1538                         if (remoteEP == null)
1539                                 throw new ArgumentNullException ("remoteEP");
1540
1541                         SocketError errorCode;
1542                         int ret = ReceiveFrom (buffer, offset, size, socketFlags, ref remoteEP, out errorCode);
1543
1544                         if (errorCode != SocketError.Success)
1545                                 throw new SocketException (errorCode);
1546
1547                         return ret;
1548                 }
1549
1550                 internal int ReceiveFrom (byte [] buffer, int offset, int size, SocketFlags socketFlags, ref EndPoint remoteEP, out SocketError errorCode)
1551                 {
1552                         SocketAddress sockaddr = remoteEP.Serialize();
1553
1554                         int nativeError;
1555                         int cnt;
1556                         unsafe {
1557                                 fixed (byte* pbuffer = buffer) {
1558                                         cnt = ReceiveFrom_internal (m_Handle, &pbuffer[offset], size, socketFlags, ref sockaddr, out nativeError, is_blocking);
1559                                 }
1560                         }
1561
1562                         errorCode = (SocketError) nativeError;
1563                         if (errorCode != SocketError.Success) {
1564                                 if (errorCode != SocketError.WouldBlock && errorCode != SocketError.InProgress) {
1565                                         is_connected = false;
1566                                 } else if (errorCode == SocketError.WouldBlock && is_blocking) { // This might happen when ReceiveTimeout is set
1567                                         errorCode = SocketError.TimedOut;
1568                                 }
1569
1570                                 return 0;
1571                         }
1572
1573                         is_connected = true;
1574                         is_bound = true;
1575
1576                         /* If sockaddr is null then we're a connection oriented protocol and should ignore the
1577                          * remoteEP parameter (see MSDN documentation for Socket.ReceiveFrom(...) ) */
1578                         if (sockaddr != null) {
1579                                 /* Stupidly, EndPoint.Create() is an instance method */
1580                                 remoteEP = remoteEP.Create (sockaddr);
1581                         }
1582
1583                         seed_endpoint = remoteEP;
1584
1585                         return cnt;
1586                 }
1587
1588                 public bool ReceiveFromAsync (SocketAsyncEventArgs e)
1589                 {
1590                         ThrowIfDisposedAndClosed ();
1591
1592                         // We do not support recv into multiple buffers yet
1593                         if (e.BufferList != null)
1594                                 throw new NotSupportedException ("Mono doesn't support using BufferList at this point.");
1595                         if (e.RemoteEndPoint == null)
1596                                 throw new ArgumentNullException ("remoteEP", "Value cannot be null.");
1597
1598                         InitSocketAsyncEventArgs (e, ReceiveFromAsyncCallback, e, SocketOperation.ReceiveFrom);
1599
1600                         e.socket_async_result.Buffer = e.Buffer;
1601                         e.socket_async_result.Offset = e.Offset;
1602                         e.socket_async_result.Size = e.Count;
1603                         e.socket_async_result.EndPoint = e.RemoteEndPoint;
1604                         e.socket_async_result.SockFlags = e.SocketFlags;
1605
1606                         QueueIOSelectorJob (ReadSem, e.socket_async_result.Handle, new IOSelectorJob (IOOperation.Read, BeginReceiveFromCallback, e.socket_async_result));
1607
1608                         return true;
1609                 }
1610
1611                 static AsyncCallback ReceiveFromAsyncCallback = new AsyncCallback (ares => {
1612                         SocketAsyncEventArgs e = (SocketAsyncEventArgs) ((SocketAsyncResult) ares).AsyncState;
1613
1614                         if (Interlocked.Exchange (ref e.in_progress, 0) != 1)
1615                                 throw new InvalidOperationException ("No operation in progress");
1616
1617                         try {
1618                                 e.BytesTransferred = e.current_socket.EndReceiveFrom (ares, ref e.remote_ep);
1619                         } catch (SocketException ex) {
1620                                 e.SocketError = ex.SocketErrorCode;
1621                         } catch (ObjectDisposedException) {
1622                                 e.SocketError = SocketError.OperationAborted;
1623                         } finally {
1624                                 e.Complete ();
1625                         }
1626                 });
1627
1628                 public IAsyncResult BeginReceiveFrom (byte[] buffer, int offset, int size, SocketFlags socketFlags, ref EndPoint remoteEP, AsyncCallback callback, object state)
1629                 {
1630                         ThrowIfDisposedAndClosed ();
1631                         ThrowIfBufferNull (buffer);
1632                         ThrowIfBufferOutOfRange (buffer, offset, size);
1633
1634                         if (remoteEP == null)
1635                                 throw new ArgumentNullException ("remoteEP");
1636
1637                         SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.ReceiveFrom) {
1638                                 Buffer = buffer,
1639                                 Offset = offset,
1640                                 Size = size,
1641                                 SockFlags = socketFlags,
1642                                 EndPoint = remoteEP,
1643                         };
1644
1645                         QueueIOSelectorJob (ReadSem, sockares.Handle, new IOSelectorJob (IOOperation.Read, BeginReceiveFromCallback, sockares));
1646
1647                         return sockares;
1648                 }
1649
1650                 static IOAsyncCallback BeginReceiveFromCallback = new IOAsyncCallback (ares => {
1651                         SocketAsyncResult sockares = (SocketAsyncResult) ares;
1652                         int total = 0;
1653
1654                         try {
1655                                 SocketError errorCode;
1656                                 total = sockares.socket.ReceiveFrom (sockares.Buffer, sockares.Offset, sockares.Size, sockares.SockFlags, ref sockares.EndPoint, out errorCode);
1657
1658                                 if (errorCode != SocketError.Success) {
1659                                         sockares.Complete (new SocketException (errorCode));
1660                                         return;
1661                                 }
1662                         } catch (Exception e) {
1663                                 sockares.Complete (e);
1664                                 return;
1665                         }
1666
1667                         sockares.Complete (total);
1668                 });
1669
1670                 public int EndReceiveFrom(IAsyncResult asyncResult, ref EndPoint endPoint)
1671                 {
1672                         ThrowIfDisposedAndClosed ();
1673
1674                         if (endPoint == null)
1675                                 throw new ArgumentNullException ("endPoint");
1676
1677                         SocketAsyncResult sockares = ValidateEndIAsyncResult (asyncResult, "EndReceiveFrom", "asyncResult");
1678
1679                         if (!sockares.IsCompleted)
1680                                 sockares.AsyncWaitHandle.WaitOne();
1681
1682                         sockares.CheckIfThrowDelayedException();
1683
1684                         endPoint = sockares.EndPoint;
1685
1686                         return sockares.Total;
1687                 }
1688
1689
1690
1691                 static unsafe int ReceiveFrom_internal (SafeSocketHandle safeHandle, byte* buffer, int count, SocketFlags flags, ref SocketAddress sockaddr, out int error, bool blocking)
1692                 {
1693                         try {
1694                                 safeHandle.RegisterForBlockingSyscall ();
1695                                 return ReceiveFrom_internal (safeHandle.DangerousGetHandle (), buffer, count, flags, ref sockaddr, out error, blocking);
1696                         } finally {
1697                                 safeHandle.UnRegisterForBlockingSyscall ();
1698                         }
1699                 }
1700
1701                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1702                 extern static unsafe int ReceiveFrom_internal(IntPtr sock, byte* buffer, int count, SocketFlags flags, ref SocketAddress sockaddr, out int error, bool blocking);
1703
1704 #endregion
1705
1706 #region ReceiveMessageFrom
1707
1708                 [MonoTODO ("Not implemented")]
1709                 public int ReceiveMessageFrom (byte[] buffer, int offset, int size, ref SocketFlags socketFlags, ref EndPoint remoteEP, out IPPacketInformation ipPacketInformation)
1710                 {
1711                         ThrowIfDisposedAndClosed ();
1712                         ThrowIfBufferNull (buffer);
1713                         ThrowIfBufferOutOfRange (buffer, offset, size);
1714
1715                         if (remoteEP == null)
1716                                 throw new ArgumentNullException ("remoteEP");
1717
1718                         // FIXME: figure out how we get hold of the IPPacketInformation
1719                         throw new NotImplementedException ();
1720                 }
1721
1722                 [MonoTODO ("Not implemented")]
1723                 public bool ReceiveMessageFromAsync (SocketAsyncEventArgs e)
1724                 {
1725                         // NO check is made whether e != null in MS.NET (NRE is thrown in such case)
1726
1727                         ThrowIfDisposedAndClosed ();
1728
1729                         throw new NotImplementedException ();
1730                 }
1731
1732                 [MonoTODO]
1733                 public IAsyncResult BeginReceiveMessageFrom (byte[] buffer, int offset, int size, SocketFlags socketFlags, ref EndPoint remoteEP, AsyncCallback callback, object state)
1734                 {
1735                         ThrowIfDisposedAndClosed ();
1736                         ThrowIfBufferNull (buffer);
1737                         ThrowIfBufferOutOfRange (buffer, offset, size);
1738
1739                         if (remoteEP == null)
1740                                 throw new ArgumentNullException ("remoteEP");
1741
1742                         throw new NotImplementedException ();
1743                 }
1744
1745                 [MonoTODO]
1746                 public int EndReceiveMessageFrom (IAsyncResult asyncResult, ref SocketFlags socketFlags, ref EndPoint endPoint, out IPPacketInformation ipPacketInformation)
1747                 {
1748                         ThrowIfDisposedAndClosed ();
1749
1750                         if (endPoint == null)
1751                                 throw new ArgumentNullException ("endPoint");
1752
1753                         /*SocketAsyncResult sockares =*/ ValidateEndIAsyncResult (asyncResult, "EndReceiveMessageFrom", "asyncResult");
1754
1755                         throw new NotImplementedException ();
1756                 }
1757
1758 #endregion
1759
1760 #region Send
1761
1762                 public int Send (byte [] buffer, int offset, int size, SocketFlags socketFlags, out SocketError errorCode)
1763                 {
1764                         ThrowIfDisposedAndClosed ();
1765                         ThrowIfBufferNull (buffer);
1766                         ThrowIfBufferOutOfRange (buffer, offset, size);
1767
1768                         if (size == 0) {
1769                                 errorCode = SocketError.Success;
1770                                 return 0;
1771                         }
1772
1773                         int nativeError;
1774                         int sent = 0;
1775                         do {
1776                                 unsafe {
1777                                         fixed (byte *pbuffer = buffer) {
1778                                                 sent += Send_internal (m_Handle, &pbuffer[offset + sent], size - sent, socketFlags, out nativeError, is_blocking);
1779                                         }
1780                                 }
1781
1782                                 errorCode = (SocketError)nativeError;
1783                                 if (errorCode != SocketError.Success && errorCode != SocketError.WouldBlock && errorCode != SocketError.InProgress) {
1784                                         is_connected = false;
1785                                         is_bound = false;
1786                                         break;
1787                                 } else {
1788                                         is_connected = true;
1789                                 }
1790                         } while (sent < size);
1791
1792                         return sent;
1793                 }
1794
1795                 [CLSCompliant (false)]
1796                 public int Send (IList<ArraySegment<byte>> buffers, SocketFlags socketFlags, out SocketError errorCode)
1797                 {
1798                         ThrowIfDisposedAndClosed ();
1799
1800                         if (buffers == null)
1801                                 throw new ArgumentNullException ("buffers");
1802                         if (buffers.Count == 0)
1803                                 throw new ArgumentException ("Buffer is empty", "buffers");
1804
1805                         int numsegments = buffers.Count;
1806                         int nativeError;
1807                         int ret;
1808
1809                         GCHandle[] gch = new GCHandle[numsegments];
1810                         try {
1811                                 unsafe {
1812                                         fixed (WSABUF* bufarray = new WSABUF[numsegments]) {
1813                                                 for(int i = 0; i < numsegments; i++) {
1814                                                         ArraySegment<byte> segment = buffers[i];
1815
1816                                                         if (segment.Offset < 0 || segment.Count < 0 || segment.Count > segment.Array.Length - segment.Offset)
1817                                                                 throw new ArgumentOutOfRangeException ("segment");
1818
1819                                                         try {} finally {
1820                                                                 gch[i] = GCHandle.Alloc (segment.Array, GCHandleType.Pinned);
1821                                                         }
1822
1823                                                         bufarray[i].len = segment.Count;
1824                                                         bufarray[i].buf = Marshal.UnsafeAddrOfPinnedArrayElement (segment.Array, segment.Offset);
1825                                                 }
1826
1827                                                 ret = Send_internal (m_Handle, bufarray, numsegments, socketFlags, out nativeError, is_blocking);
1828                                         }
1829                                 }
1830                         } finally {
1831                                 for (int i = 0; i < numsegments; i++) {
1832                                         if (gch[i].IsAllocated)
1833                                                 gch[i].Free();
1834                                 }
1835                         }
1836
1837                         errorCode = (SocketError)nativeError;
1838
1839                         return ret;
1840                 }
1841
1842                 public bool SendAsync (SocketAsyncEventArgs e)
1843                 {
1844                         // NO check is made whether e != null in MS.NET (NRE is thrown in such case)
1845
1846                         ThrowIfDisposedAndClosed ();
1847
1848                         if (e.Buffer == null && e.BufferList == null)
1849                                 throw new NullReferenceException ("Either e.Buffer or e.BufferList must be valid buffers.");
1850
1851                         if (e.Buffer == null) {
1852                                 InitSocketAsyncEventArgs (e, SendAsyncCallback, e, SocketOperation.SendGeneric);
1853
1854                                 e.socket_async_result.Buffers = e.BufferList;
1855
1856                                 QueueIOSelectorJob (WriteSem, e.socket_async_result.Handle, new IOSelectorJob (IOOperation.Write, BeginSendGenericCallback, e.socket_async_result));
1857                         } else {
1858                                 InitSocketAsyncEventArgs (e, SendAsyncCallback, e, SocketOperation.Send);
1859
1860                                 e.socket_async_result.Buffer = e.Buffer;
1861                                 e.socket_async_result.Offset = e.Offset;
1862                                 e.socket_async_result.Size = e.Count;
1863
1864                                 QueueIOSelectorJob (WriteSem, e.socket_async_result.Handle, new IOSelectorJob (IOOperation.Write, s => BeginSendCallback ((SocketAsyncResult) s, 0), e.socket_async_result));
1865                         }
1866
1867                         return true;
1868                 }
1869
1870                 static AsyncCallback SendAsyncCallback = new AsyncCallback (ares => {
1871                         SocketAsyncEventArgs e = (SocketAsyncEventArgs) ((SocketAsyncResult) ares).AsyncState;
1872
1873                         if (Interlocked.Exchange (ref e.in_progress, 0) != 1)
1874                                 throw new InvalidOperationException ("No operation in progress");
1875
1876                         try {
1877                                 e.BytesTransferred = e.current_socket.EndSend (ares);
1878                         } catch (SocketException se){
1879                                 e.SocketError = se.SocketErrorCode;
1880                         } catch (ObjectDisposedException) {
1881                                 e.SocketError = SocketError.OperationAborted;
1882                         } finally {
1883                                 e.Complete ();
1884                         }
1885                 });
1886
1887                 public IAsyncResult BeginSend (byte[] buffer, int offset, int size, SocketFlags socketFlags, out SocketError errorCode, AsyncCallback callback, object state)
1888                 {
1889                         ThrowIfDisposedAndClosed ();
1890                         ThrowIfBufferNull (buffer);
1891                         ThrowIfBufferOutOfRange (buffer, offset, size);
1892
1893                         if (!is_connected) {
1894                                 errorCode = SocketError.NotConnected;
1895                                 return null;
1896                         }
1897
1898                         errorCode = SocketError.Success;
1899
1900                         SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.Send) {
1901                                 Buffer = buffer,
1902                                 Offset = offset,
1903                                 Size = size,
1904                                 SockFlags = socketFlags,
1905                         };
1906
1907                         QueueIOSelectorJob (WriteSem, sockares.Handle, new IOSelectorJob (IOOperation.Write, s => BeginSendCallback ((SocketAsyncResult) s, 0), sockares));
1908
1909                         return sockares;
1910                 }
1911
1912                 static void BeginSendCallback (SocketAsyncResult sockares, int sent_so_far)
1913                 {
1914                         int total = 0;
1915
1916                         try {
1917                                 unsafe {
1918                                         fixed (byte *pbuffer = sockares.Buffer) {
1919                                                 total = Socket.Send_internal (sockares.socket.m_Handle, &pbuffer[sockares.Offset], sockares.Size, sockares.SockFlags, out sockares.error, false);
1920                                         }
1921                                 }
1922                         } catch (Exception e) {
1923                                 sockares.Complete (e);
1924                                 return;
1925                         }
1926
1927                         if (sockares.error == 0) {
1928                                 sent_so_far += total;
1929                                 sockares.Offset += total;
1930                                 sockares.Size -= total;
1931
1932                                 if (sockares.socket.CleanedUp) {
1933                                         sockares.Complete (total);
1934                                         return;
1935                                 }
1936
1937                                 if (sockares.Size > 0) {
1938                                         IOSelector.Add (sockares.Handle, new IOSelectorJob (IOOperation.Write, s => BeginSendCallback ((SocketAsyncResult) s, sent_so_far), sockares));
1939                                         return; // Have to finish writing everything. See bug #74475.
1940                                 }
1941
1942                                 sockares.Total = sent_so_far;
1943                         }
1944
1945                         sockares.Complete (total);
1946                 }
1947
1948                 [CLSCompliant (false)]
1949                 public IAsyncResult BeginSend (IList<ArraySegment<byte>> buffers, SocketFlags socketFlags, out SocketError errorCode, AsyncCallback callback, object state)
1950                 {
1951                         ThrowIfDisposedAndClosed ();
1952
1953                         if (buffers == null)
1954                                 throw new ArgumentNullException ("buffers");
1955
1956                         if (!is_connected) {
1957                                 errorCode = SocketError.NotConnected;
1958                                 return null;
1959                         }
1960
1961                         errorCode = SocketError.Success;
1962
1963                         SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.SendGeneric) {
1964                                 Buffers = buffers,
1965                                 SockFlags = socketFlags,
1966                         };
1967
1968                         QueueIOSelectorJob (WriteSem, sockares.Handle, new IOSelectorJob (IOOperation.Write, BeginSendGenericCallback, sockares));
1969
1970                         return sockares;
1971                 }
1972
1973                 static IOAsyncCallback BeginSendGenericCallback = new IOAsyncCallback (ares => {
1974                         SocketAsyncResult sockares = (SocketAsyncResult) ares;
1975                         int total = 0;
1976
1977                         try {
1978                                 total = sockares.socket.Send (sockares.Buffers, sockares.SockFlags);
1979                         } catch (Exception e) {
1980                                 sockares.Complete (e);
1981                                 return;
1982                         }
1983
1984                         sockares.Complete (total);
1985                 });
1986
1987                 public int EndSend (IAsyncResult asyncResult, out SocketError errorCode)
1988                 {
1989                         ThrowIfDisposedAndClosed ();
1990
1991                         SocketAsyncResult sockares = ValidateEndIAsyncResult (asyncResult, "EndSend", "asyncResult");
1992
1993                         if (!sockares.IsCompleted)
1994                                 sockares.AsyncWaitHandle.WaitOne ();
1995
1996                         errorCode = sockares.ErrorCode;
1997
1998                         if (errorCode != SocketError.Success && errorCode != SocketError.WouldBlock && errorCode != SocketError.InProgress)
1999                                 is_connected = false;
2000
2001                         /* If no socket error occurred, call CheckIfThrowDelayedException in
2002                          * case there are other kinds of exceptions that should be thrown.*/
2003                         if (errorCode == SocketError.Success)
2004                                 sockares.CheckIfThrowDelayedException ();
2005
2006                         return sockares.Total;
2007                 }
2008
2009                 static unsafe int Send_internal (SafeSocketHandle safeHandle, WSABUF* bufarray, int count, SocketFlags flags, out int error, bool blocking)
2010                 {
2011                         try {
2012                                 safeHandle.RegisterForBlockingSyscall ();
2013                                 return Send_internal (safeHandle.DangerousGetHandle (), bufarray, count, flags, out error, blocking);
2014                         } finally {
2015                                 safeHandle.UnRegisterForBlockingSyscall ();
2016                         }
2017                 }
2018
2019                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2020                 extern static unsafe int Send_internal (IntPtr sock, WSABUF* bufarray, int count, SocketFlags flags, out int error, bool blocking);
2021
2022                 static unsafe int Send_internal (SafeSocketHandle safeHandle, byte* buffer, int count, SocketFlags flags, out int error, bool blocking)
2023                 {
2024                         try {
2025                                 safeHandle.RegisterForBlockingSyscall ();
2026                                 return Send_internal (safeHandle.DangerousGetHandle (), buffer, count, flags, out error, blocking);
2027                         } finally {
2028                                 safeHandle.UnRegisterForBlockingSyscall ();
2029                         }
2030                 }
2031
2032                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
2033                 extern static unsafe int Send_internal(IntPtr sock, byte* buffer, int count, SocketFlags flags, out int error, bool blocking);
2034
2035 #endregion
2036
2037 #region SendTo
2038
2039                 public int SendTo (byte [] buffer, int offset, int size, SocketFlags socketFlags, EndPoint remoteEP)
2040                 {
2041                         ThrowIfDisposedAndClosed ();
2042                         ThrowIfBufferNull (buffer);
2043                         ThrowIfBufferOutOfRange (buffer, offset, size);
2044
2045                         if (remoteEP == null)
2046                                 throw new ArgumentNullException("remoteEP");
2047
2048                         int error;
2049                         int ret;
2050                         unsafe {
2051                                 fixed (byte *pbuffer = buffer) {
2052                                         ret = SendTo_internal (m_Handle, &pbuffer[offset], size, socketFlags, remoteEP.Serialize (), out error, is_blocking);
2053                                 }
2054                         }
2055
2056                         SocketError err = (SocketError) error;
2057                         if (err != 0) {
2058                                 if (err != SocketError.WouldBlock && err != SocketError.InProgress)
2059                                         is_connected = false;
2060                                 throw new SocketException (error);
2061                         }
2062
2063                         is_connected = true;
2064                         is_bound = true;
2065                         seed_endpoint = remoteEP;
2066
2067                         return ret;
2068                 }
2069
2070                 public bool SendToAsync (SocketAsyncEventArgs e)
2071                 {
2072                         // NO check is made whether e != null in MS.NET (NRE is thrown in such case)
2073
2074                         ThrowIfDisposedAndClosed ();
2075
2076                         if (e.BufferList != null)
2077                                 throw new NotSupportedException ("Mono doesn't support using BufferList at this point.");
2078                         if (e.RemoteEndPoint == null)
2079                                 throw new ArgumentNullException ("remoteEP", "Value cannot be null.");
2080
2081                         InitSocketAsyncEventArgs (e, SendToAsyncCallback, e, SocketOperation.SendTo);
2082
2083                         e.socket_async_result.Buffer = e.Buffer;
2084                         e.socket_async_result.Offset = e.Offset;
2085                         e.socket_async_result.Size = e.Count;
2086                         e.socket_async_result.SockFlags = e.SocketFlags;
2087                         e.socket_async_result.EndPoint = e.RemoteEndPoint;
2088
2089                         QueueIOSelectorJob (WriteSem, e.socket_async_result.Handle, new IOSelectorJob (IOOperation.Write, s => BeginSendToCallback ((SocketAsyncResult) s, 0), e.socket_async_result));
2090
2091                         return true;
2092                 }
2093
2094                 static AsyncCallback SendToAsyncCallback = new AsyncCallback (ares => {
2095                         SocketAsyncEventArgs e = (SocketAsyncEventArgs) ((SocketAsyncResult) ares).AsyncState;
2096
2097                         if (Interlocked.Exchange (ref e.in_progress, 0) != 1)
2098                                 throw new InvalidOperationException ("No operation in progress");
2099
2100                         try {
2101                                 e.BytesTransferred = e.current_socket.EndSendTo (ares);
2102                         } catch (SocketException ex) {
2103                                 e.SocketError = ex.SocketErrorCode;
2104                         } catch (ObjectDisposedException) {
2105                                 e.SocketError = SocketError.OperationAborted;
2106                         } finally {
2107                                 e.Complete ();
2108                         }
2109                 });
2110
2111                 public IAsyncResult BeginSendTo(byte[] buffer, int offset, int size, SocketFlags socketFlags, EndPoint remoteEP, AsyncCallback callback, object state)
2112                 {
2113                         ThrowIfDisposedAndClosed ();
2114                         ThrowIfBufferNull (buffer);
2115                         ThrowIfBufferOutOfRange (buffer, offset, size);
2116
2117                         SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.SendTo) {
2118                                 Buffer = buffer,
2119                                 Offset = offset,
2120                                 Size = size,
2121                                 SockFlags = socketFlags,
2122                                 EndPoint = remoteEP,
2123                         };
2124
2125                         QueueIOSelectorJob (WriteSem, sockares.Handle, new IOSelectorJob (IOOperation.Write, s => BeginSendToCallback ((SocketAsyncResult) s, 0), sockares));
2126
2127                         return sockares;
2128                 }
2129
2130                 static void BeginSendToCallback (SocketAsyncResult sockares, int sent_so_far)
2131                 {
2132                         int total = 0;
2133                         try {
2134                                 total = sockares.socket.SendTo (sockares.Buffer, sockares.Offset, sockares.Size, sockares.SockFlags, sockares.EndPoint);
2135
2136                                 if (sockares.error == 0) {
2137                                         sent_so_far += total;
2138                                         sockares.Offset += total;
2139                                         sockares.Size -= total;
2140                                 }
2141
2142                                 if (sockares.Size > 0) {
2143                                         IOSelector.Add (sockares.Handle, new IOSelectorJob (IOOperation.Write, s => BeginSendToCallback ((SocketAsyncResult) s, sent_so_far), sockares));
2144                                         return; // Have to finish writing everything. See bug #74475.
2145                                 }
2146
2147                                 sockares.Total = sent_so_far;
2148                         } catch (Exception e) {
2149                                 sockares.Complete (e);
2150                                 return;
2151                         }
2152
2153                         sockares.Complete ();
2154                 }
2155
2156                 public int EndSendTo (IAsyncResult asyncResult)
2157                 {
2158                         ThrowIfDisposedAndClosed ();
2159
2160                         SocketAsyncResult sockares = ValidateEndIAsyncResult (asyncResult, "EndSendTo", "result");
2161
2162                         if (!sockares.IsCompleted)
2163                                 sockares.AsyncWaitHandle.WaitOne();
2164
2165                         sockares.CheckIfThrowDelayedException();
2166
2167                         return sockares.Total;
2168                 }
2169
2170                 static unsafe int SendTo_internal (SafeSocketHandle safeHandle, byte* buffer, int count, SocketFlags flags, SocketAddress sa, out int error, bool blocking)
2171                 {
2172                         try {
2173                                 safeHandle.RegisterForBlockingSyscall ();
2174                                 return SendTo_internal (safeHandle.DangerousGetHandle (), buffer, count, flags, sa, out error, blocking);
2175                         } finally {
2176                                 safeHandle.UnRegisterForBlockingSyscall ();
2177                         }
2178                 }
2179
2180                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
2181                 extern static unsafe int SendTo_internal (IntPtr sock, byte* buffer, int count, SocketFlags flags, SocketAddress sa, out int error, bool blocking);
2182
2183 #endregion
2184
2185 #region SendFile
2186
2187                 public void SendFile (string fileName, byte[] preBuffer, byte[] postBuffer, TransmitFileOptions flags)
2188                 {
2189                         ThrowIfDisposedAndClosed ();
2190
2191                         if (!is_connected)
2192                                 throw new NotSupportedException ();
2193                         if (!is_blocking)
2194                                 throw new InvalidOperationException ();
2195
2196                         int error = 0;
2197                         if (!SendFile_internal (m_Handle, fileName, preBuffer, postBuffer, flags, out error, is_blocking) || error != 0) {
2198                                 SocketException exc = new SocketException (error);
2199                                 if (exc.ErrorCode == 2 || exc.ErrorCode == 3)
2200                                         throw new FileNotFoundException ();
2201                                 throw exc;
2202                         }
2203                 }
2204
2205                 public IAsyncResult BeginSendFile (string fileName, byte[] preBuffer, byte[] postBuffer, TransmitFileOptions flags, AsyncCallback callback, object state)
2206                 {
2207                         ThrowIfDisposedAndClosed ();
2208
2209                         if (!is_connected)
2210                                 throw new NotSupportedException ();
2211                         if (!File.Exists (fileName))
2212                                 throw new FileNotFoundException ();
2213
2214                         SendFileHandler handler = new SendFileHandler (SendFile);
2215
2216                         return new SendFileAsyncResult (handler, handler.BeginInvoke (fileName, preBuffer, postBuffer, flags, ar => callback (new SendFileAsyncResult (handler, ar)), state));
2217                 }
2218
2219                 public void EndSendFile (IAsyncResult asyncResult)
2220                 {
2221                         ThrowIfDisposedAndClosed ();
2222
2223                         if (asyncResult == null)
2224                                 throw new ArgumentNullException ("asyncResult");
2225
2226                         SendFileAsyncResult ares = asyncResult as SendFileAsyncResult;
2227                         if (ares == null)
2228                                 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
2229
2230                         ares.Delegate.EndInvoke (ares.Original);
2231                 }
2232
2233                 static bool SendFile_internal (SafeSocketHandle safeHandle, string filename, byte [] pre_buffer, byte [] post_buffer, TransmitFileOptions flags, out int error, bool blocking)
2234                 {
2235                         try {
2236                                 safeHandle.RegisterForBlockingSyscall ();
2237                                 return SendFile_internal (safeHandle.DangerousGetHandle (), filename, pre_buffer, post_buffer, flags, out error, blocking);
2238                         } finally {
2239                                 safeHandle.UnRegisterForBlockingSyscall ();
2240                         }
2241                 }
2242
2243                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
2244                 extern static bool SendFile_internal (IntPtr sock, string filename, byte [] pre_buffer, byte [] post_buffer, TransmitFileOptions flags, out int error, bool blocking);
2245
2246                 delegate void SendFileHandler (string fileName, byte [] preBuffer, byte [] postBuffer, TransmitFileOptions flags);
2247
2248                 sealed class SendFileAsyncResult : IAsyncResult {
2249                         IAsyncResult ares;
2250                         SendFileHandler d;
2251
2252                         public SendFileAsyncResult (SendFileHandler d, IAsyncResult ares)
2253                         {
2254                                 this.d = d;
2255                                 this.ares = ares;
2256                         }
2257
2258                         public object AsyncState {
2259                                 get { return ares.AsyncState; }
2260                         }
2261
2262                         public WaitHandle AsyncWaitHandle {
2263                                 get { return ares.AsyncWaitHandle; }
2264                         }
2265
2266                         public bool CompletedSynchronously {
2267                                 get { return ares.CompletedSynchronously; }
2268                         }
2269
2270                         public bool IsCompleted {
2271                                 get { return ares.IsCompleted; }
2272                         }
2273
2274                         public SendFileHandler Delegate {
2275                                 get { return d; }
2276                         }
2277
2278                         public IAsyncResult Original {
2279                                 get { return ares; }
2280                         }
2281                 }
2282
2283 #endregion
2284
2285 #region SendPackets
2286
2287                 [MonoTODO ("Not implemented")]
2288                 public bool SendPacketsAsync (SocketAsyncEventArgs e)
2289                 {
2290                         // NO check is made whether e != null in MS.NET (NRE is thrown in such case)
2291
2292                         ThrowIfDisposedAndClosed ();
2293
2294                         throw new NotImplementedException ();
2295                 }
2296
2297 #endregion
2298
2299 #region DuplicateAndClose
2300
2301                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
2302                 static extern bool Duplicate_internal(IntPtr handle, int targetProcessId, out IntPtr duplicateHandle, out MonoIOError error);
2303
2304                 [MonoLimitation ("We do not support passing sockets across processes, we merely allow this API to pass the socket across AppDomains")]
2305                 public SocketInformation DuplicateAndClose (int targetProcessId)
2306                 {
2307                         var si = new SocketInformation ();
2308                         si.Options =
2309                                 (is_listening      ? SocketInformationOptions.Listening : 0) |
2310                                 (is_connected      ? SocketInformationOptions.Connected : 0) |
2311                                 (is_blocking       ? 0 : SocketInformationOptions.NonBlocking) |
2312                                 (useOverlappedIO ? SocketInformationOptions.UseOnlyOverlappedIO : 0);
2313
2314                         IntPtr duplicateHandle;
2315                         if (!Duplicate_internal (Handle, targetProcessId, out duplicateHandle, out MonoIOError error))
2316                                 throw MonoIO.GetException (error);
2317
2318                         si.ProtocolInformation = Mono.DataConverter.Pack ("iiiil", (int)addressFamily, (int)socketType, (int)protocolType, is_bound ? 1 : 0, (long)duplicateHandle);
2319                         m_Handle = null;
2320  
2321                         return si;
2322                 }
2323
2324 #endregion
2325
2326 #region GetSocketOption
2327
2328                 public void GetSocketOption (SocketOptionLevel optionLevel, SocketOptionName optionName, byte [] optionValue)
2329                 {
2330                         ThrowIfDisposedAndClosed ();
2331
2332                         if (optionValue == null)
2333                                 throw new SocketException ((int) SocketError.Fault, "Error trying to dereference an invalid pointer");
2334
2335                         int error;
2336                         GetSocketOption_arr_internal (m_Handle, optionLevel, optionName, ref optionValue, out error);
2337
2338                         if (error != 0)
2339                                 throw new SocketException (error);
2340                 }
2341
2342                 public byte [] GetSocketOption (SocketOptionLevel optionLevel, SocketOptionName optionName, int optionLength)
2343                 {
2344                         ThrowIfDisposedAndClosed ();
2345
2346                         int error;
2347                         byte[] byte_val = new byte [optionLength];
2348                         GetSocketOption_arr_internal (m_Handle, optionLevel, optionName, ref byte_val, out error);
2349
2350                         if (error != 0)
2351                                 throw new SocketException (error);
2352
2353                         return byte_val;
2354                 }
2355
2356                 public object GetSocketOption (SocketOptionLevel optionLevel, SocketOptionName optionName)
2357                 {
2358                         ThrowIfDisposedAndClosed ();
2359
2360                         int error;
2361                         object obj_val;
2362                         GetSocketOption_obj_internal (m_Handle, optionLevel, optionName, out obj_val, out error);
2363
2364                         if (error != 0)
2365                                 throw new SocketException (error);
2366
2367                         if (optionName == SocketOptionName.Linger)
2368                                 return (LingerOption) obj_val;
2369                         else if (optionName == SocketOptionName.AddMembership || optionName == SocketOptionName.DropMembership)
2370                                 return (MulticastOption) obj_val;
2371                         else if (obj_val is int)
2372                                 return (int) obj_val;
2373                         else
2374                                 return obj_val;
2375                 }
2376
2377                 static void GetSocketOption_arr_internal (SafeSocketHandle safeHandle, SocketOptionLevel level, SocketOptionName name, ref byte[] byte_val, out int error)
2378                 {
2379                         bool release = false;
2380                         try {
2381                                 safeHandle.DangerousAddRef (ref release);
2382                                 GetSocketOption_arr_internal (safeHandle.DangerousGetHandle (), level, name, ref byte_val, out error);
2383                         } finally {
2384                                 if (release)
2385                                         safeHandle.DangerousRelease ();
2386                         }
2387                 }
2388
2389                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
2390                 extern static void GetSocketOption_arr_internal(IntPtr socket, SocketOptionLevel level, SocketOptionName name, ref byte[] byte_val, out int error);
2391
2392                 static void GetSocketOption_obj_internal (SafeSocketHandle safeHandle, SocketOptionLevel level, SocketOptionName name, out object obj_val, out int error)
2393                 {
2394                         bool release = false;
2395                         try {
2396                                 safeHandle.DangerousAddRef (ref release);
2397                                 GetSocketOption_obj_internal (safeHandle.DangerousGetHandle (), level, name, out obj_val, out error);
2398                         } finally {
2399                                 if (release)
2400                                         safeHandle.DangerousRelease ();
2401                         }
2402                 }
2403
2404                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
2405                 extern static void GetSocketOption_obj_internal(IntPtr socket, SocketOptionLevel level, SocketOptionName name, out object obj_val, out int error);
2406
2407 #endregion
2408
2409 #region SetSocketOption
2410
2411                 public void SetSocketOption (SocketOptionLevel optionLevel, SocketOptionName optionName, byte [] optionValue)
2412                 {
2413                         ThrowIfDisposedAndClosed ();
2414
2415                         // I'd throw an ArgumentNullException, but this is what MS does.
2416                         if (optionValue == null)
2417                                 throw new SocketException ((int) SocketError.Fault, "Error trying to dereference an invalid pointer");
2418
2419                         int error;
2420                         SetSocketOption_internal (m_Handle, optionLevel, optionName, null, optionValue, 0, out error);
2421
2422                         if (error != 0) {
2423                                 if (error == (int) SocketError.InvalidArgument)
2424                                         throw new ArgumentException ();
2425                                 throw new SocketException (error);
2426                         }
2427                 }
2428
2429                 public void SetSocketOption (SocketOptionLevel optionLevel, SocketOptionName optionName, object optionValue)
2430                 {
2431                         ThrowIfDisposedAndClosed ();
2432
2433                         // NOTE: if a null is passed, the byte[] overload is used instead...
2434                         if (optionValue == null)
2435                                 throw new ArgumentNullException("optionValue");
2436
2437                         int error;
2438
2439                         if (optionLevel == SocketOptionLevel.Socket && optionName == SocketOptionName.Linger) {
2440                                 LingerOption linger = optionValue as LingerOption;
2441                                 if (linger == null)
2442                                         throw new ArgumentException ("A 'LingerOption' value must be specified.", "optionValue");
2443                                 SetSocketOption_internal (m_Handle, optionLevel, optionName, linger, null, 0, out error);
2444                         } else if (optionLevel == SocketOptionLevel.IP && (optionName == SocketOptionName.AddMembership || optionName == SocketOptionName.DropMembership)) {
2445                                 MulticastOption multicast = optionValue as MulticastOption;
2446                                 if (multicast == null)
2447                                         throw new ArgumentException ("A 'MulticastOption' value must be specified.", "optionValue");
2448                                 SetSocketOption_internal (m_Handle, optionLevel, optionName, multicast, null, 0, out error);
2449                         } else if (optionLevel == SocketOptionLevel.IPv6 && (optionName == SocketOptionName.AddMembership || optionName == SocketOptionName.DropMembership)) {
2450                                 IPv6MulticastOption multicast = optionValue as IPv6MulticastOption;
2451                                 if (multicast == null)
2452                                         throw new ArgumentException ("A 'IPv6MulticastOption' value must be specified.", "optionValue");
2453                                 SetSocketOption_internal (m_Handle, optionLevel, optionName, multicast, null, 0, out error);
2454                         } else {
2455                                 throw new ArgumentException ("Invalid value specified.", "optionValue");
2456                         }
2457
2458                         if (error != 0) {
2459                                 if (error == (int) SocketError.InvalidArgument)
2460                                         throw new ArgumentException ();
2461                                 throw new SocketException (error);
2462                         }
2463                 }
2464
2465                 public void SetSocketOption (SocketOptionLevel optionLevel, SocketOptionName optionName, bool optionValue)
2466                 {
2467                         int int_val = optionValue ? 1 : 0;
2468
2469                         SetSocketOption (optionLevel, optionName, int_val);
2470                 }
2471
2472                 public void SetSocketOption (SocketOptionLevel optionLevel, SocketOptionName optionName, int optionValue)
2473                 {
2474                         ThrowIfDisposedAndClosed ();
2475
2476                         int error;
2477                         SetSocketOption_internal (m_Handle, optionLevel, optionName, null, null, optionValue, out error);
2478
2479                         if (error != 0) {
2480                                 if (error == (int) SocketError.InvalidArgument)
2481                                         throw new ArgumentException ();
2482                                 throw new SocketException (error);
2483                         }
2484                 }
2485
2486                 static void SetSocketOption_internal (SafeSocketHandle safeHandle, SocketOptionLevel level, SocketOptionName name, object obj_val, byte [] byte_val, int int_val, out int error)
2487                 {
2488                         bool release = false;
2489                         try {
2490                                 safeHandle.DangerousAddRef (ref release);
2491                                 SetSocketOption_internal (safeHandle.DangerousGetHandle (), level, name, obj_val, byte_val, int_val, out error);
2492                         } finally {
2493                                 if (release)
2494                                         safeHandle.DangerousRelease ();
2495                         }
2496                 }
2497
2498                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
2499                 extern static void SetSocketOption_internal (IntPtr socket, SocketOptionLevel level, SocketOptionName name, object obj_val, byte [] byte_val, int int_val, out int error);
2500
2501 #endregion
2502
2503 #region IOControl
2504
2505                 public int IOControl (int ioControlCode, byte [] optionInValue, byte [] optionOutValue)
2506                 {
2507                         if (CleanedUp)
2508                                 throw new ObjectDisposedException (GetType ().ToString ());
2509
2510                         int error;
2511                         int result = IOControl_internal (m_Handle, ioControlCode, optionInValue, optionOutValue, out error);
2512
2513                         if (error != 0)
2514                                 throw new SocketException (error);
2515                         if (result == -1)
2516                                 throw new InvalidOperationException ("Must use Blocking property instead.");
2517
2518                         return result;
2519                 }
2520
2521                 static int IOControl_internal (SafeSocketHandle safeHandle, int ioctl_code, byte [] input, byte [] output, out int error)
2522                 {
2523                         bool release = false;
2524                         try {
2525                                 safeHandle.DangerousAddRef (ref release);
2526                                 return IOControl_internal (safeHandle.DangerousGetHandle (), ioctl_code, input, output, out error);
2527                         } finally {
2528                                 if (release)
2529                                         safeHandle.DangerousRelease ();
2530                         }
2531                 }
2532
2533                 /* See Socket.IOControl, WSAIoctl documentation in MSDN. The common options between UNIX
2534                  * and Winsock are FIONREAD, FIONBIO and SIOCATMARK. Anything else will depend on the system
2535                  * except SIO_KEEPALIVE_VALS which is properly handled on both windows and linux. */
2536                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
2537                 extern static int IOControl_internal (IntPtr sock, int ioctl_code, byte [] input, byte [] output, out int error);
2538
2539 #endregion
2540
2541 #region Close
2542
2543                 public void Close ()
2544                 {
2545                         linger_timeout = 0;
2546                         Dispose ();
2547                 }
2548
2549                 public void Close (int timeout)
2550                 {
2551                         linger_timeout = timeout;
2552                         Dispose ();
2553                 }
2554
2555                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
2556                 internal extern static void Close_internal (IntPtr socket, out int error);
2557
2558 #endregion
2559
2560 #region Shutdown
2561
2562                 public void Shutdown (SocketShutdown how)
2563                 {
2564                         ThrowIfDisposedAndClosed ();
2565
2566                         if (!is_connected)
2567                                 throw new SocketException (10057); // Not connected
2568
2569                         int error;
2570                         Shutdown_internal (m_Handle, how, out error);
2571
2572                         if (error != 0)
2573                                 throw new SocketException (error);
2574                 }
2575
2576                 static void Shutdown_internal (SafeSocketHandle safeHandle, SocketShutdown how, out int error)
2577                 {
2578                         bool release = false;
2579                         try {
2580                                 safeHandle.DangerousAddRef (ref release);
2581                                 Shutdown_internal (safeHandle.DangerousGetHandle (), how, out error);
2582                         } finally {
2583                                 if (release)
2584                                         safeHandle.DangerousRelease ();
2585                         }
2586                 }
2587
2588                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2589                 internal extern static void Shutdown_internal (IntPtr socket, SocketShutdown how, out int error);
2590
2591 #endregion
2592
2593 #region Dispose
2594
2595                 protected virtual void Dispose (bool disposing)
2596                 {
2597                         if (CleanedUp)
2598                                 return;
2599
2600                         m_IntCleanedUp = 1;
2601                         bool was_connected = is_connected;
2602                         is_connected = false;
2603
2604                         if (m_Handle != null) {
2605                                 is_closed = true;
2606                                 IntPtr x = Handle;
2607
2608                                 if (was_connected)
2609                                         Linger (x);
2610
2611                                 m_Handle.Dispose ();
2612                         }
2613                 }
2614
2615                 void Linger (IntPtr handle)
2616                 {
2617                         if (!is_connected || linger_timeout <= 0)
2618                                 return;
2619
2620                         /* We don't want to receive any more data */
2621                         int error;
2622                         Shutdown_internal (handle, SocketShutdown.Receive, out error);
2623
2624                         if (error != 0)
2625                                 return;
2626
2627                         int seconds = linger_timeout / 1000;
2628                         int ms = linger_timeout % 1000;
2629                         if (ms > 0) {
2630                                 /* If the other end closes, this will return 'true' with 'Available' == 0 */
2631                                 Poll_internal (handle, SelectMode.SelectRead, ms * 1000, out error);
2632                                 if (error != 0)
2633                                         return;
2634                         }
2635
2636                         if (seconds > 0) {
2637                                 LingerOption linger = new LingerOption (true, seconds);
2638                                 SetSocketOption_internal (handle, SocketOptionLevel.Socket, SocketOptionName.Linger, linger, null, 0, out error);
2639                                 /* Not needed, we're closing upon return */
2640                                 //if (error != 0)
2641                                 //      return;
2642                         }
2643                 }
2644
2645 #endregion
2646
2647                 void ThrowIfDisposedAndClosed (Socket socket)
2648                 {
2649                         if (socket.CleanedUp && socket.is_closed)
2650                                 throw new ObjectDisposedException (socket.GetType ().ToString ());
2651                 }
2652
2653                 void ThrowIfDisposedAndClosed ()
2654                 {
2655                         if (CleanedUp && is_closed)
2656                                 throw new ObjectDisposedException (GetType ().ToString ());
2657                 }
2658
2659                 void ThrowIfBufferNull (byte[] buffer)
2660                 {
2661                         if (buffer == null)
2662                                 throw new ArgumentNullException ("buffer");
2663                 }
2664
2665                 void ThrowIfBufferOutOfRange (byte[] buffer, int offset, int size)
2666                 {
2667                         if (offset < 0)
2668                                 throw new ArgumentOutOfRangeException ("offset", "offset must be >= 0");
2669                         if (offset > buffer.Length)
2670                                 throw new ArgumentOutOfRangeException ("offset", "offset must be <= buffer.Length");
2671                         if (size < 0)
2672                                 throw new ArgumentOutOfRangeException ("size", "size must be >= 0");
2673                         if (size > buffer.Length - offset)
2674                                 throw new ArgumentOutOfRangeException ("size", "size must be <= buffer.Length - offset");
2675                 }
2676
2677                 void ThrowIfUdp ()
2678                 {
2679                         if (protocolType == ProtocolType.Udp)
2680                                 throw new SocketException ((int)SocketError.ProtocolOption);
2681                 }
2682
2683                 SocketAsyncResult ValidateEndIAsyncResult (IAsyncResult ares, string methodName, string argName)
2684                 {
2685                         if (ares == null)
2686                                 throw new ArgumentNullException (argName);
2687
2688                         SocketAsyncResult sockares = ares as SocketAsyncResult;
2689                         if (sockares == null)
2690                                 throw new ArgumentException ("Invalid IAsyncResult", argName);
2691                         if (Interlocked.CompareExchange (ref sockares.EndCalled, 1, 0) == 1)
2692                                 throw new InvalidOperationException (methodName + " can only be called once per asynchronous operation");
2693
2694                         return sockares;
2695                 }
2696
2697                 void QueueIOSelectorJob (SemaphoreSlim sem, IntPtr handle, IOSelectorJob job)
2698                 {
2699                         var task = sem.WaitAsync();
2700                         // fast path without Task<Action> allocation.
2701                         if (task.IsCompleted) {
2702                                 if (CleanedUp) {
2703                                         job.MarkDisposed ();
2704                                         return;
2705                                 }
2706                                 IOSelector.Add (handle, job);
2707                         }
2708                         else
2709                         {
2710                                 task.ContinueWith( t => {
2711                                         if (CleanedUp) {
2712                                                 job.MarkDisposed ();
2713                                                 return;
2714                                         }
2715                                         IOSelector.Add(handle, job);
2716                                 });
2717                         }
2718                 }
2719
2720                 void InitSocketAsyncEventArgs (SocketAsyncEventArgs e, AsyncCallback callback, object state, SocketOperation operation)
2721                 {
2722                         e.socket_async_result.Init (this, callback, state, operation);
2723                         if (e.AcceptSocket != null) {
2724                                 e.socket_async_result.AcceptSocket = e.AcceptSocket;
2725                         }
2726                         e.current_socket = this;
2727                         e.SetLastOperation (SocketOperationToSocketAsyncOperation (operation));
2728                         e.SocketError = SocketError.Success;
2729                         e.BytesTransferred = 0;
2730                 }
2731
2732                 SocketAsyncOperation SocketOperationToSocketAsyncOperation (SocketOperation op)
2733                 {
2734                         switch (op) {
2735                         case SocketOperation.Connect:
2736                                 return SocketAsyncOperation.Connect;
2737                         case SocketOperation.Accept:
2738                                 return SocketAsyncOperation.Accept;
2739                         case SocketOperation.Disconnect:
2740                                 return SocketAsyncOperation.Disconnect;
2741                         case SocketOperation.Receive:
2742                         case SocketOperation.ReceiveGeneric:
2743                                 return SocketAsyncOperation.Receive;
2744                         case SocketOperation.ReceiveFrom:
2745                                 return SocketAsyncOperation.ReceiveFrom;
2746                         case SocketOperation.Send:
2747                         case SocketOperation.SendGeneric:
2748                                 return SocketAsyncOperation.Send;
2749                         case SocketOperation.SendTo:
2750                                 return SocketAsyncOperation.SendTo;
2751                         default:
2752                                 throw new NotImplementedException (String.Format ("Operation {0} is not implemented", op));
2753                         }
2754                 }
2755                 
2756                 IPEndPoint RemapIPEndPoint (IPEndPoint input) {
2757                         // If socket is DualMode ensure we automatically handle mapping IPv4 addresses to IPv6.
2758                         if (IsDualMode && input.AddressFamily == AddressFamily.InterNetwork)
2759                                 return new IPEndPoint (input.Address.MapToIPv6 (), input.Port);
2760                         
2761                         return input;
2762                 }
2763                 
2764                 [StructLayout (LayoutKind.Sequential)]
2765                 struct WSABUF {
2766                         public int len;
2767                         public IntPtr buf;
2768                 }
2769
2770                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
2771                 internal static extern void cancel_blocking_socket_operation (Thread thread);
2772
2773                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
2774                 internal static extern bool SupportsPortReuse (ProtocolType proto);
2775
2776                 internal static int FamilyHint {
2777                         get {
2778                                 // Returns one of
2779                                 //      MONO_HINT_UNSPECIFIED           = 0,
2780                                 //      MONO_HINT_IPV4                          = 1,
2781                                 //      MONO_HINT_IPV6                          = 2,
2782
2783                                 int hint = 0;
2784                                 if (OSSupportsIPv4) {
2785                                         hint = 1;
2786                                 }
2787
2788                                 if (OSSupportsIPv6) {
2789                                         hint = hint == 0 ? 2 : 0;
2790                                 }
2791
2792                                 return hint;
2793                         }
2794                 }
2795
2796                 static bool IsProtocolSupported (NetworkInterfaceComponent networkInterface)
2797                 {
2798 #if MOBILE
2799                         return true;
2800 #else
2801                         var nics = NetworkInterface.GetAllNetworkInterfaces ();
2802                         foreach (var adapter in nics) {
2803                                 if (adapter.Supports (networkInterface))
2804                                         return true;
2805                         }
2806
2807                         return false;
2808 #endif
2809                 }
2810         }
2811 }
2812