89911c426b4420c4cc944c0dea38db8850a40204
[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                         // Bug #75154: Connect() should not succeed for .Any addresses.
980                         if (remoteEP is IPEndPoint) {
981                                 IPEndPoint ep = (IPEndPoint) remoteEP;
982                                 if (ep.Address.Equals (IPAddress.Any) || ep.Address.Equals (IPAddress.IPv6Any)) {
983                                         sockares.Complete (new SocketException ((int) SocketError.AddressNotAvailable), true);
984                                         return sockares;
985                                 }
986
987                                 sockares.EndPoint = remoteEP = RemapIPEndPoint (ep);
988                         }
989
990                         int error = 0;
991
992                         if (connect_in_progress) {
993                                 // This could happen when multiple IPs are used
994                                 // Calling connect() again will reset the connection attempt and cause
995                                 // an error. Better to just close the socket and move on.
996                                 connect_in_progress = false;
997                                 m_Handle.Dispose ();
998                                 m_Handle = new SafeSocketHandle (Socket_internal (addressFamily, socketType, protocolType, out error), true);
999                                 if (error != 0)
1000                                         throw new SocketException (error);
1001                         }
1002
1003                         bool blk = is_blocking;
1004                         if (blk)
1005                                 Blocking = false;
1006                         Connect_internal (m_Handle, remoteEP.Serialize (), out error, false);
1007                         if (blk)
1008                                 Blocking = true;
1009
1010                         if (error == 0) {
1011                                 // succeeded synch
1012                                 is_connected = true;
1013                                 is_bound = true;
1014                                 sockares.Complete (true);
1015                                 return sockares;
1016                         }
1017
1018                         if (error != (int) SocketError.InProgress && error != (int) SocketError.WouldBlock) {
1019                                 // error synch
1020                                 is_connected = false;
1021                                 is_bound = false;
1022                                 sockares.Complete (new SocketException (error), true);
1023                                 return sockares;
1024                         }
1025
1026                         // continue asynch
1027                         is_connected = false;
1028                         is_bound = false;
1029                         connect_in_progress = true;
1030
1031                         IOSelector.Add (sockares.Handle, new IOSelectorJob (IOOperation.Write, BeginConnectCallback, sockares));
1032
1033                         return sockares;
1034                 }
1035
1036                 public IAsyncResult BeginConnect (IPAddress[] addresses, int port, AsyncCallback requestCallback, object state)
1037                 {
1038                         ThrowIfDisposedAndClosed ();
1039
1040                         if (addresses == null)
1041                                 throw new ArgumentNullException ("addresses");
1042                         if (addresses.Length == 0)
1043                                 throw new ArgumentException ("Empty addresses list");
1044                         if (this.AddressFamily != AddressFamily.InterNetwork && this.AddressFamily != AddressFamily.InterNetworkV6)
1045                                 throw new NotSupportedException ("This method is only valid for addresses in the InterNetwork or InterNetworkV6 families");
1046                         if (port <= 0 || port > 65535)
1047                                 throw new ArgumentOutOfRangeException ("port", "Must be > 0 and < 65536");
1048                         if (is_listening)
1049                                 throw new InvalidOperationException ();
1050
1051                         SocketAsyncResult sockares = new SocketAsyncResult (this, requestCallback, state, SocketOperation.Connect) {
1052                                 Addresses = addresses,
1053                                 Port = port,
1054                         };
1055
1056                         is_connected = false;
1057
1058                         return BeginMConnect (sockares);
1059                 }
1060
1061                 internal IAsyncResult BeginMConnect (SocketAsyncResult sockares)
1062                 {
1063                         SocketAsyncResult ares = null;
1064                         Exception exc = null;
1065                         AsyncCallback callback;
1066
1067                         for (int i = sockares.CurrentAddress; i < sockares.Addresses.Length; i++) {
1068                                 try {
1069                                         sockares.CurrentAddress++;
1070
1071                                         ares = (SocketAsyncResult) BeginConnect (new IPEndPoint (sockares.Addresses [i], sockares.Port), null, sockares);
1072                                         if (ares.IsCompleted && ares.CompletedSynchronously) {
1073                                                 ares.CheckIfThrowDelayedException ();
1074
1075                                                 callback = ares.AsyncCallback;
1076                                                 if (callback != null)
1077                                                         ThreadPool.UnsafeQueueUserWorkItem (_ => callback (ares), null);
1078                                         }
1079
1080                                         break;
1081                                 } catch (Exception e) {
1082                                         exc = e;
1083                                         ares = null;
1084                                 }
1085                         }
1086
1087                         if (ares == null)
1088                                 throw exc;
1089
1090                         return sockares;
1091                 }
1092
1093                 static IOAsyncCallback BeginConnectCallback = new IOAsyncCallback (ares => {
1094                         SocketAsyncResult sockares = (SocketAsyncResult) ares;
1095
1096                         if (sockares.EndPoint == null) {
1097                                 sockares.Complete (new SocketException ((int)SocketError.AddressNotAvailable));
1098                                 return;
1099                         }
1100
1101                         SocketAsyncResult mconnect = sockares.AsyncState as SocketAsyncResult;
1102                         bool is_mconnect = mconnect != null && mconnect.Addresses != null;
1103
1104                         try {
1105                                 EndPoint ep = sockares.EndPoint;
1106                                 int error_code = (int) sockares.socket.GetSocketOption (SocketOptionLevel.Socket, SocketOptionName.Error);
1107
1108                                 if (error_code == 0) {
1109                                         if (is_mconnect)
1110                                                 sockares = mconnect;
1111
1112                                         sockares.socket.seed_endpoint = ep;
1113                                         sockares.socket.is_connected = true;
1114                                         sockares.socket.is_bound = true;
1115                                         sockares.socket.connect_in_progress = false;
1116                                         sockares.error = 0;
1117                                         sockares.Complete ();
1118                                         return;
1119                                 }
1120
1121                                 if (!is_mconnect) {
1122                                         sockares.socket.connect_in_progress = false;
1123                                         sockares.Complete (new SocketException (error_code));
1124                                         return;
1125                                 }
1126
1127                                 if (mconnect.CurrentAddress >= mconnect.Addresses.Length) {
1128                                         mconnect.Complete (new SocketException (error_code));
1129                                         return;
1130                                 }
1131
1132                                 mconnect.socket.BeginMConnect (mconnect);
1133                         } catch (Exception e) {
1134                                 sockares.socket.connect_in_progress = false;
1135
1136                                 if (is_mconnect)
1137                                         sockares = mconnect;
1138
1139                                 sockares.Complete (e);
1140                                 return;
1141                         }
1142                 });
1143
1144                 public void EndConnect (IAsyncResult asyncResult)
1145                 {
1146                         ThrowIfDisposedAndClosed ();
1147
1148                         SocketAsyncResult sockares = ValidateEndIAsyncResult (asyncResult, "EndConnect", "asyncResult");
1149
1150                         if (!sockares.IsCompleted)
1151                                 sockares.AsyncWaitHandle.WaitOne();
1152
1153                         sockares.CheckIfThrowDelayedException();
1154                 }
1155
1156                 static void Connect_internal (SafeSocketHandle safeHandle, SocketAddress sa, out int error, bool blocking)
1157                 {
1158                         try {
1159                                 safeHandle.RegisterForBlockingSyscall ();
1160                                 Connect_internal (safeHandle.DangerousGetHandle (), sa, out error, blocking);
1161                         } finally {
1162                                 safeHandle.UnRegisterForBlockingSyscall ();
1163                         }
1164                 }
1165
1166                 /* Connects to the remote address */
1167                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1168                 extern static void Connect_internal(IntPtr sock, SocketAddress sa, out int error, bool blocking);
1169
1170                 /* Returns :
1171                  *  - false when it is ok to use RemoteEndPoint
1172                  *  - true when addresses must be used (and addresses could be null/empty) */
1173                 bool GetCheckedIPs (SocketAsyncEventArgs e, out IPAddress [] addresses)
1174                 {
1175                         addresses = null;
1176
1177                         // Connect to the first address that match the host name, like:
1178                         // http://blogs.msdn.com/ncl/archive/2009/07/20/new-ncl-features-in-net-4-0-beta-2.aspx
1179                         // while skipping entries that do not match the address family
1180                         DnsEndPoint dep = e.RemoteEndPoint as DnsEndPoint;
1181                         if (dep != null) {
1182                                 addresses = Dns.GetHostAddresses (dep.Host);
1183                                 return true;
1184                         } else {
1185                                 e.ConnectByNameError = null;
1186                                 return false;
1187                         }
1188                 }
1189
1190 #endregion
1191
1192 #region Disconnect
1193
1194                 /* According to the docs, the MS runtime will throw PlatformNotSupportedException
1195                  * if the platform is newer than w2k.  We should be able to cope... */
1196                 public void Disconnect (bool reuseSocket)
1197                 {
1198                         ThrowIfDisposedAndClosed ();
1199
1200                         int error = 0;
1201                         Disconnect_internal (m_Handle, reuseSocket, out error);
1202
1203                         if (error != 0) {
1204                                 if (error == 50) {
1205                                         /* ERROR_NOT_SUPPORTED */
1206                                         throw new PlatformNotSupportedException ();
1207                                 } else {
1208                                         throw new SocketException (error);
1209                                 }
1210                         }
1211
1212                         is_connected = false;
1213                         if (reuseSocket) {
1214                                 /* Do managed housekeeping here... */
1215                         }
1216                 }
1217
1218                 public bool DisconnectAsync (SocketAsyncEventArgs e)
1219                 {
1220                         // NO check is made whether e != null in MS.NET (NRE is thrown in such case)
1221
1222                         ThrowIfDisposedAndClosed ();
1223
1224                         InitSocketAsyncEventArgs (e, DisconnectAsyncCallback, e, SocketOperation.Disconnect);
1225
1226                         IOSelector.Add (e.socket_async_result.Handle, new IOSelectorJob (IOOperation.Write, BeginDisconnectCallback, e.socket_async_result));
1227
1228                         return true;
1229                 }
1230
1231                 static AsyncCallback DisconnectAsyncCallback = new AsyncCallback (ares => {
1232                         SocketAsyncEventArgs e = (SocketAsyncEventArgs) ((SocketAsyncResult) ares).AsyncState;
1233
1234                         if (Interlocked.Exchange (ref e.in_progress, 0) != 1)
1235                                 throw new InvalidOperationException ("No operation in progress");
1236
1237                         try {
1238                                 e.current_socket.EndDisconnect (ares);
1239                         } catch (SocketException ex) {
1240                                 e.SocketError = ex.SocketErrorCode;
1241                         } catch (ObjectDisposedException) {
1242                                 e.SocketError = SocketError.OperationAborted;
1243                         } finally {
1244                                 e.Complete ();
1245                         }
1246                 });
1247
1248                 public IAsyncResult BeginDisconnect (bool reuseSocket, AsyncCallback callback, object state)
1249                 {
1250                         ThrowIfDisposedAndClosed ();
1251
1252                         SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.Disconnect) {
1253                                 ReuseSocket = reuseSocket,
1254                         };
1255
1256                         IOSelector.Add (sockares.Handle, new IOSelectorJob (IOOperation.Write, BeginDisconnectCallback, sockares));
1257
1258                         return sockares;
1259                 }
1260
1261                 static IOAsyncCallback BeginDisconnectCallback = new IOAsyncCallback (ares => {
1262                         SocketAsyncResult sockares = (SocketAsyncResult) ares;
1263
1264                         try {
1265                                 sockares.socket.Disconnect (sockares.ReuseSocket);
1266                         } catch (Exception e) {
1267                                 sockares.Complete (e);
1268                                 return;
1269                         }
1270
1271                         sockares.Complete ();
1272                 });
1273
1274                 public void EndDisconnect (IAsyncResult asyncResult)
1275                 {
1276                         ThrowIfDisposedAndClosed ();
1277
1278                         SocketAsyncResult sockares = ValidateEndIAsyncResult (asyncResult, "EndDisconnect", "asyncResult");
1279
1280                         if (!sockares.IsCompleted)
1281                                 sockares.AsyncWaitHandle.WaitOne ();
1282
1283                         sockares.CheckIfThrowDelayedException ();
1284                 }
1285
1286                 static void Disconnect_internal (SafeSocketHandle safeHandle, bool reuse, out int error)
1287                 {
1288                         bool release = false;
1289                         try {
1290                                 safeHandle.DangerousAddRef (ref release);
1291                                 Disconnect_internal (safeHandle.DangerousGetHandle (), reuse, out error);
1292                         } finally {
1293                                 if (release)
1294                                         safeHandle.DangerousRelease ();
1295                         }
1296                 }
1297
1298                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1299                 extern static void Disconnect_internal (IntPtr sock, bool reuse, out int error);
1300
1301 #endregion
1302
1303 #region Receive
1304
1305                 public int Receive (byte [] buffer, int offset, int size, SocketFlags socketFlags, out SocketError errorCode)
1306                 {
1307                         ThrowIfDisposedAndClosed ();
1308                         ThrowIfBufferNull (buffer);
1309                         ThrowIfBufferOutOfRange (buffer, offset, size);
1310
1311                         int nativeError;
1312                         int ret = Receive_internal (m_Handle, buffer, offset, size, socketFlags, out nativeError, is_blocking);
1313
1314                         errorCode = (SocketError) nativeError;
1315                         if (errorCode != SocketError.Success && errorCode != SocketError.WouldBlock && errorCode != SocketError.InProgress) {
1316                                 is_connected = false;
1317                                 is_bound = false;
1318                         } else {
1319                                 is_connected = true;
1320                         }
1321
1322                         return ret;
1323                 }
1324
1325                 [CLSCompliant (false)]
1326                 public int Receive (IList<ArraySegment<byte>> buffers, SocketFlags socketFlags, out SocketError errorCode)
1327                 {
1328                         ThrowIfDisposedAndClosed ();
1329
1330                         if (buffers == null || buffers.Count == 0)
1331                                 throw new ArgumentNullException ("buffers");
1332
1333                         int numsegments = buffers.Count;
1334                         int nativeError;
1335                         int ret;
1336
1337                         /* Only example I can find of sending a byte array reference directly into an internal
1338                          * call is in System.Runtime.Remoting/System.Runtime.Remoting.Channels.Ipc.Win32/NamedPipeSocket.cs,
1339                          * so taking a lead from that... */
1340                         WSABUF[] bufarray = new WSABUF[numsegments];
1341                         GCHandle[] gch = new GCHandle[numsegments];
1342
1343                         for (int i = 0; i < numsegments; i++) {
1344                                 ArraySegment<byte> segment = buffers[i];
1345
1346                                 if (segment.Offset < 0 || segment.Count < 0 || segment.Count > segment.Array.Length - segment.Offset)
1347                                         throw new ArgumentOutOfRangeException ("segment");
1348
1349                                 gch[i] = GCHandle.Alloc (segment.Array, GCHandleType.Pinned);
1350                                 bufarray[i].len = segment.Count;
1351                                 bufarray[i].buf = Marshal.UnsafeAddrOfPinnedArrayElement (segment.Array, segment.Offset);
1352                         }
1353
1354                         try {
1355                                 ret = Receive_internal (m_Handle, bufarray, socketFlags, out nativeError, is_blocking);
1356                         } finally {
1357                                 for (int i = 0; i < numsegments; i++) {
1358                                         if (gch[i].IsAllocated)
1359                                                 gch[i].Free ();
1360                                 }
1361                         }
1362
1363                         errorCode = (SocketError) nativeError;
1364
1365                         return ret;
1366                 }
1367
1368                 public bool ReceiveAsync (SocketAsyncEventArgs e)
1369                 {
1370                         // NO check is made whether e != null in MS.NET (NRE is thrown in such case)
1371
1372                         ThrowIfDisposedAndClosed ();
1373
1374                         // LAME SPEC: the ArgumentException is never thrown, instead an NRE is
1375                         // thrown when e.Buffer and e.BufferList are null (works fine when one is
1376                         // set to a valid object)
1377                         if (e.Buffer == null && e.BufferList == null)
1378                                 throw new NullReferenceException ("Either e.Buffer or e.BufferList must be valid buffers.");
1379
1380                         if (e.Buffer == null) {
1381                                 InitSocketAsyncEventArgs (e, ReceiveAsyncCallback, e, SocketOperation.ReceiveGeneric);
1382
1383                                 e.socket_async_result.Buffers = e.BufferList;
1384
1385                                 QueueIOSelectorJob (ReadSem, e.socket_async_result.Handle, new IOSelectorJob (IOOperation.Read, BeginReceiveGenericCallback, e.socket_async_result));
1386                         } else {
1387                                 InitSocketAsyncEventArgs (e, ReceiveAsyncCallback, e, SocketOperation.Receive);
1388
1389                                 e.socket_async_result.Buffer = e.Buffer;
1390                                 e.socket_async_result.Offset = e.Offset;
1391                                 e.socket_async_result.Size = e.Count;
1392
1393                                 QueueIOSelectorJob (ReadSem, e.socket_async_result.Handle, new IOSelectorJob (IOOperation.Read, BeginReceiveCallback, e.socket_async_result));
1394                         }
1395
1396                         return true;
1397                 }
1398
1399                 static AsyncCallback ReceiveAsyncCallback = new AsyncCallback (ares => {
1400                         SocketAsyncEventArgs e = (SocketAsyncEventArgs) ((SocketAsyncResult) ares).AsyncState;
1401
1402                         if (Interlocked.Exchange (ref e.in_progress, 0) != 1)
1403                                 throw new InvalidOperationException ("No operation in progress");
1404
1405                         try {
1406                                 e.BytesTransferred = e.current_socket.EndReceive (ares);
1407                         } catch (SocketException se){
1408                                 e.SocketError = se.SocketErrorCode;
1409                         } catch (ObjectDisposedException) {
1410                                 e.SocketError = SocketError.OperationAborted;
1411                         } finally {
1412                                 e.Complete ();
1413                         }
1414                 });
1415
1416                 public IAsyncResult BeginReceive (byte[] buffer, int offset, int size, SocketFlags socketFlags, out SocketError errorCode, AsyncCallback callback, object state)
1417                 {
1418                         ThrowIfDisposedAndClosed ();
1419                         ThrowIfBufferNull (buffer);
1420                         ThrowIfBufferOutOfRange (buffer, offset, size);
1421
1422                         /* As far as I can tell from the docs and from experimentation, a pointer to the
1423                          * SocketError parameter is not supposed to be saved for the async parts.  And as we don't
1424                          * set any socket errors in the setup code, we just have to set it to Success. */
1425                         errorCode = SocketError.Success;
1426
1427                         SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.Receive) {
1428                                 Buffer = buffer,
1429                                 Offset = offset,
1430                                 Size = size,
1431                                 SockFlags = socketFlags,
1432                         };
1433
1434                         QueueIOSelectorJob (ReadSem, sockares.Handle, new IOSelectorJob (IOOperation.Read, BeginReceiveCallback, sockares));
1435
1436                         return sockares;
1437                 }
1438
1439                 static IOAsyncCallback BeginReceiveCallback = new IOAsyncCallback (ares => {
1440                         SocketAsyncResult sockares = (SocketAsyncResult) ares;
1441                         int total = 0;
1442
1443                         try {
1444                                 total = Receive_internal (sockares.socket.m_Handle, sockares.Buffer, sockares.Offset, sockares.Size, sockares.SockFlags, out sockares.error, sockares.socket.is_blocking);
1445                         } catch (Exception e) {
1446                                 sockares.Complete (e);
1447                                 return;
1448                         }
1449
1450                         sockares.Complete (total);
1451                 });
1452
1453                 [CLSCompliant (false)]
1454                 public IAsyncResult BeginReceive (IList<ArraySegment<byte>> buffers, SocketFlags socketFlags, out SocketError errorCode, AsyncCallback callback, object state)
1455                 {
1456                         ThrowIfDisposedAndClosed ();
1457
1458                         if (buffers == null)
1459                                 throw new ArgumentNullException ("buffers");
1460
1461                         /* I assume the same SocketError semantics as above */
1462                         errorCode = SocketError.Success;
1463
1464                         SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.ReceiveGeneric) {
1465                                 Buffers = buffers,
1466                                 SockFlags = socketFlags,
1467                         };
1468
1469                         QueueIOSelectorJob (ReadSem, sockares.Handle, new IOSelectorJob (IOOperation.Read, BeginReceiveGenericCallback, sockares));
1470
1471                         return sockares;
1472                 }
1473
1474                 static IOAsyncCallback BeginReceiveGenericCallback = new IOAsyncCallback (ares => {
1475                         SocketAsyncResult sockares = (SocketAsyncResult) ares;
1476                         int total = 0;
1477
1478                         try {
1479                                 total = sockares.socket.Receive (sockares.Buffers, sockares.SockFlags);
1480                         } catch (Exception e) {
1481                                 sockares.Complete (e);
1482                                 return;
1483                         }
1484
1485                         sockares.Complete (total);
1486                 });
1487
1488                 public int EndReceive (IAsyncResult asyncResult, out SocketError errorCode)
1489                 {
1490                         ThrowIfDisposedAndClosed ();
1491
1492                         SocketAsyncResult sockares = ValidateEndIAsyncResult (asyncResult, "EndReceive", "asyncResult");
1493
1494                         if (!sockares.IsCompleted)
1495                                 sockares.AsyncWaitHandle.WaitOne ();
1496
1497                         errorCode = sockares.ErrorCode;
1498
1499                         if (errorCode != SocketError.Success && errorCode != SocketError.WouldBlock && errorCode != SocketError.InProgress)
1500                                 is_connected = false;
1501
1502                         // If no socket error occurred, call CheckIfThrowDelayedException in case there are other
1503                         // kinds of exceptions that should be thrown.
1504                         if (errorCode == SocketError.Success)
1505                                 sockares.CheckIfThrowDelayedException();
1506
1507                         return sockares.Total;
1508                 }
1509
1510                 static int Receive_internal (SafeSocketHandle safeHandle, WSABUF[] bufarray, SocketFlags flags, out int error, bool blocking)
1511                 {
1512                         try {
1513                                 safeHandle.RegisterForBlockingSyscall ();
1514                                 return Receive_internal (safeHandle.DangerousGetHandle (), bufarray, flags, out error, blocking);
1515                         } finally {
1516                                 safeHandle.UnRegisterForBlockingSyscall ();
1517                         }
1518                 }
1519
1520                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
1521                 extern static int Receive_internal (IntPtr sock, WSABUF[] bufarray, SocketFlags flags, out int error, bool blocking);
1522
1523                 static int Receive_internal (SafeSocketHandle safeHandle, byte[] buffer, int offset, int count, SocketFlags flags, out int error, bool blocking)
1524                 {
1525                         try {
1526                                 safeHandle.RegisterForBlockingSyscall ();
1527                                 return Receive_internal (safeHandle.DangerousGetHandle (), buffer, offset, count, flags, out error, blocking);
1528                         } finally {
1529                                 safeHandle.UnRegisterForBlockingSyscall ();
1530                         }
1531                 }
1532
1533                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1534                 extern static int Receive_internal(IntPtr sock, byte[] buffer, int offset, int count, SocketFlags flags, out int error, bool blocking);
1535
1536 #endregion
1537
1538 #region ReceiveFrom
1539
1540                 public int ReceiveFrom (byte [] buffer, int offset, int size, SocketFlags socketFlags, ref EndPoint remoteEP)
1541                 {
1542                         ThrowIfDisposedAndClosed ();
1543                         ThrowIfBufferNull (buffer);
1544                         ThrowIfBufferOutOfRange (buffer, offset, size);
1545
1546                         if (remoteEP == null)
1547                                 throw new ArgumentNullException ("remoteEP");
1548
1549                         SocketError errorCode;
1550                         int ret = ReceiveFrom (buffer, offset, size, socketFlags, ref remoteEP, out errorCode);
1551
1552                         if (errorCode != SocketError.Success)
1553                                 throw new SocketException (errorCode);
1554
1555                         return ret;
1556                 }
1557
1558                 internal int ReceiveFrom (byte [] buffer, int offset, int size, SocketFlags socketFlags, ref EndPoint remoteEP, out SocketError errorCode)
1559                 {
1560                         SocketAddress sockaddr = remoteEP.Serialize();
1561
1562                         int nativeError;
1563                         int cnt = ReceiveFrom_internal (m_Handle, buffer, offset, size, socketFlags, ref sockaddr, out nativeError, is_blocking);
1564
1565                         errorCode = (SocketError) nativeError;
1566                         if (errorCode != SocketError.Success) {
1567                                 if (errorCode != SocketError.WouldBlock && errorCode != SocketError.InProgress) {
1568                                         is_connected = false;
1569                                 } else if (errorCode == SocketError.WouldBlock && is_blocking) { // This might happen when ReceiveTimeout is set
1570                                         errorCode = SocketError.TimedOut;
1571                                 }
1572
1573                                 return 0;
1574                         }
1575
1576                         is_connected = true;
1577                         is_bound = true;
1578
1579                         /* If sockaddr is null then we're a connection oriented protocol and should ignore the
1580                          * remoteEP parameter (see MSDN documentation for Socket.ReceiveFrom(...) ) */
1581                         if (sockaddr != null) {
1582                                 /* Stupidly, EndPoint.Create() is an instance method */
1583                                 remoteEP = remoteEP.Create (sockaddr);
1584                         }
1585
1586                         seed_endpoint = remoteEP;
1587
1588                         return cnt;
1589                 }
1590
1591                 public bool ReceiveFromAsync (SocketAsyncEventArgs e)
1592                 {
1593                         ThrowIfDisposedAndClosed ();
1594
1595                         // We do not support recv into multiple buffers yet
1596                         if (e.BufferList != null)
1597                                 throw new NotSupportedException ("Mono doesn't support using BufferList at this point.");
1598                         if (e.RemoteEndPoint == null)
1599                                 throw new ArgumentNullException ("remoteEP", "Value cannot be null.");
1600
1601                         InitSocketAsyncEventArgs (e, ReceiveFromAsyncCallback, e, SocketOperation.ReceiveFrom);
1602
1603                         e.socket_async_result.Buffer = e.Buffer;
1604                         e.socket_async_result.Offset = e.Offset;
1605                         e.socket_async_result.Size = e.Count;
1606                         e.socket_async_result.EndPoint = e.RemoteEndPoint;
1607                         e.socket_async_result.SockFlags = e.SocketFlags;
1608
1609                         QueueIOSelectorJob (ReadSem, e.socket_async_result.Handle, new IOSelectorJob (IOOperation.Read, BeginReceiveFromCallback, e.socket_async_result));
1610
1611                         return true;
1612                 }
1613
1614                 static AsyncCallback ReceiveFromAsyncCallback = new AsyncCallback (ares => {
1615                         SocketAsyncEventArgs e = (SocketAsyncEventArgs) ((SocketAsyncResult) ares).AsyncState;
1616
1617                         if (Interlocked.Exchange (ref e.in_progress, 0) != 1)
1618                                 throw new InvalidOperationException ("No operation in progress");
1619
1620                         try {
1621                                 e.BytesTransferred = e.current_socket.EndReceiveFrom (ares, ref e.remote_ep);
1622                         } catch (SocketException ex) {
1623                                 e.SocketError = ex.SocketErrorCode;
1624                         } catch (ObjectDisposedException) {
1625                                 e.SocketError = SocketError.OperationAborted;
1626                         } finally {
1627                                 e.Complete ();
1628                         }
1629                 });
1630
1631                 public IAsyncResult BeginReceiveFrom (byte[] buffer, int offset, int size, SocketFlags socketFlags, ref EndPoint remoteEP, AsyncCallback callback, object state)
1632                 {
1633                         ThrowIfDisposedAndClosed ();
1634                         ThrowIfBufferNull (buffer);
1635                         ThrowIfBufferOutOfRange (buffer, offset, size);
1636
1637                         if (remoteEP == null)
1638                                 throw new ArgumentNullException ("remoteEP");
1639
1640                         SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.ReceiveFrom) {
1641                                 Buffer = buffer,
1642                                 Offset = offset,
1643                                 Size = size,
1644                                 SockFlags = socketFlags,
1645                                 EndPoint = remoteEP,
1646                         };
1647
1648                         QueueIOSelectorJob (ReadSem, sockares.Handle, new IOSelectorJob (IOOperation.Read, BeginReceiveFromCallback, sockares));
1649
1650                         return sockares;
1651                 }
1652
1653                 static IOAsyncCallback BeginReceiveFromCallback = new IOAsyncCallback (ares => {
1654                         SocketAsyncResult sockares = (SocketAsyncResult) ares;
1655                         int total = 0;
1656
1657                         try {
1658                                 SocketError errorCode;
1659                                 total = sockares.socket.ReceiveFrom (sockares.Buffer, sockares.Offset, sockares.Size, sockares.SockFlags, ref sockares.EndPoint, out errorCode);
1660
1661                                 if (errorCode != SocketError.Success) {
1662                                         sockares.Complete (new SocketException (errorCode));
1663                                         return;
1664                                 }
1665                         } catch (Exception e) {
1666                                 sockares.Complete (e);
1667                                 return;
1668                         }
1669
1670                         sockares.Complete (total);
1671                 });
1672
1673                 public int EndReceiveFrom(IAsyncResult asyncResult, ref EndPoint endPoint)
1674                 {
1675                         ThrowIfDisposedAndClosed ();
1676
1677                         if (endPoint == null)
1678                                 throw new ArgumentNullException ("endPoint");
1679
1680                         SocketAsyncResult sockares = ValidateEndIAsyncResult (asyncResult, "EndReceiveFrom", "asyncResult");
1681
1682                         if (!sockares.IsCompleted)
1683                                 sockares.AsyncWaitHandle.WaitOne();
1684
1685                         sockares.CheckIfThrowDelayedException();
1686
1687                         endPoint = sockares.EndPoint;
1688
1689                         return sockares.Total;
1690                 }
1691
1692
1693
1694                 static int ReceiveFrom_internal (SafeSocketHandle safeHandle, byte[] buffer, int offset, int count, SocketFlags flags, ref SocketAddress sockaddr, out int error, bool blocking)
1695                 {
1696                         try {
1697                                 safeHandle.RegisterForBlockingSyscall ();
1698                                 return ReceiveFrom_internal (safeHandle.DangerousGetHandle (), buffer, offset, count, flags, ref sockaddr, out error, blocking);
1699                         } finally {
1700                                 safeHandle.UnRegisterForBlockingSyscall ();
1701                         }
1702                 }
1703
1704                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1705                 extern static int ReceiveFrom_internal(IntPtr sock, byte[] buffer, int offset, int count, SocketFlags flags, ref SocketAddress sockaddr, out int error, bool blocking);
1706
1707 #endregion
1708
1709 #region ReceiveMessageFrom
1710
1711                 [MonoTODO ("Not implemented")]
1712                 public int ReceiveMessageFrom (byte[] buffer, int offset, int size, ref SocketFlags socketFlags, ref EndPoint remoteEP, out IPPacketInformation ipPacketInformation)
1713                 {
1714                         ThrowIfDisposedAndClosed ();
1715                         ThrowIfBufferNull (buffer);
1716                         ThrowIfBufferOutOfRange (buffer, offset, size);
1717
1718                         if (remoteEP == null)
1719                                 throw new ArgumentNullException ("remoteEP");
1720
1721                         // FIXME: figure out how we get hold of the IPPacketInformation
1722                         throw new NotImplementedException ();
1723                 }
1724
1725                 [MonoTODO ("Not implemented")]
1726                 public bool ReceiveMessageFromAsync (SocketAsyncEventArgs e)
1727                 {
1728                         // NO check is made whether e != null in MS.NET (NRE is thrown in such case)
1729
1730                         ThrowIfDisposedAndClosed ();
1731
1732                         throw new NotImplementedException ();
1733                 }
1734
1735                 [MonoTODO]
1736                 public IAsyncResult BeginReceiveMessageFrom (byte[] buffer, int offset, int size, SocketFlags socketFlags, ref EndPoint remoteEP, AsyncCallback callback, object state)
1737                 {
1738                         ThrowIfDisposedAndClosed ();
1739                         ThrowIfBufferNull (buffer);
1740                         ThrowIfBufferOutOfRange (buffer, offset, size);
1741
1742                         if (remoteEP == null)
1743                                 throw new ArgumentNullException ("remoteEP");
1744
1745                         throw new NotImplementedException ();
1746                 }
1747
1748                 [MonoTODO]
1749                 public int EndReceiveMessageFrom (IAsyncResult asyncResult, ref SocketFlags socketFlags, ref EndPoint endPoint, out IPPacketInformation ipPacketInformation)
1750                 {
1751                         ThrowIfDisposedAndClosed ();
1752
1753                         if (endPoint == null)
1754                                 throw new ArgumentNullException ("endPoint");
1755
1756                         /*SocketAsyncResult sockares =*/ ValidateEndIAsyncResult (asyncResult, "EndReceiveMessageFrom", "asyncResult");
1757
1758                         throw new NotImplementedException ();
1759                 }
1760
1761 #endregion
1762
1763 #region Send
1764
1765                 public int Send (byte [] buffer, int offset, int size, SocketFlags socketFlags, out SocketError errorCode)
1766                 {
1767                         ThrowIfDisposedAndClosed ();
1768                         ThrowIfBufferNull (buffer);
1769                         ThrowIfBufferOutOfRange (buffer, offset, size);
1770
1771                         if (size == 0) {
1772                                 errorCode = SocketError.Success;
1773                                 return 0;
1774                         }
1775
1776                         int nativeError;
1777                         int sent = 0;
1778                         do {
1779                                 sent += Send_internal (
1780 m_Handle, buffer, offset + sent, size - sent, socketFlags, out nativeError, is_blocking);
1781                                 errorCode = (SocketError)nativeError;
1782                                 if (errorCode != SocketError.Success && errorCode != SocketError.WouldBlock && errorCode != SocketError.InProgress) {
1783                                         is_connected = false;
1784                                         is_bound = false;
1785                                         break;
1786                                 } else {
1787                                         is_connected = true;
1788                                 }
1789                         } while (sent < size);
1790
1791                         return sent;
1792                 }
1793
1794                 [CLSCompliant (false)]
1795                 public int Send (IList<ArraySegment<byte>> buffers, SocketFlags socketFlags, out SocketError errorCode)
1796                 {
1797                         ThrowIfDisposedAndClosed ();
1798
1799                         if (buffers == null)
1800                                 throw new ArgumentNullException ("buffers");
1801                         if (buffers.Count == 0)
1802                                 throw new ArgumentException ("Buffer is empty", "buffers");
1803
1804                         int numsegments = buffers.Count;
1805                         int nativeError;
1806                         int ret;
1807
1808                         WSABUF[] bufarray = new WSABUF[numsegments];
1809                         GCHandle[] gch = new GCHandle[numsegments];
1810
1811                         for(int i = 0; i < numsegments; i++) {
1812                                 ArraySegment<byte> segment = buffers[i];
1813
1814                                 if (segment.Offset < 0 || segment.Count < 0 || segment.Count > segment.Array.Length - segment.Offset)
1815                                         throw new ArgumentOutOfRangeException ("segment");
1816
1817                                 gch[i] = GCHandle.Alloc (segment.Array, GCHandleType.Pinned);
1818                                 bufarray[i].len = segment.Count;
1819                                 bufarray[i].buf = Marshal.UnsafeAddrOfPinnedArrayElement (segment.Array, segment.Offset);
1820                         }
1821
1822                         try {
1823                                 ret = Send_internal (m_Handle, bufarray, socketFlags, out nativeError, is_blocking);
1824                         } finally {
1825                                 for(int i = 0; i < numsegments; i++) {
1826                                         if (gch[i].IsAllocated) {
1827                                                 gch[i].Free ();
1828                                         }
1829                                 }
1830                         }
1831
1832                         errorCode = (SocketError)nativeError;
1833
1834                         return ret;
1835                 }
1836
1837                 public bool SendAsync (SocketAsyncEventArgs e)
1838                 {
1839                         // NO check is made whether e != null in MS.NET (NRE is thrown in such case)
1840
1841                         ThrowIfDisposedAndClosed ();
1842
1843                         if (e.Buffer == null && e.BufferList == null)
1844                                 throw new NullReferenceException ("Either e.Buffer or e.BufferList must be valid buffers.");
1845
1846                         if (e.Buffer == null) {
1847                                 InitSocketAsyncEventArgs (e, SendAsyncCallback, e, SocketOperation.SendGeneric);
1848
1849                                 e.socket_async_result.Buffers = e.BufferList;
1850
1851                                 QueueIOSelectorJob (WriteSem, e.socket_async_result.Handle, new IOSelectorJob (IOOperation.Write, BeginSendGenericCallback, e.socket_async_result));
1852                         } else {
1853                                 InitSocketAsyncEventArgs (e, SendAsyncCallback, e, SocketOperation.Send);
1854
1855                                 e.socket_async_result.Buffer = e.Buffer;
1856                                 e.socket_async_result.Offset = e.Offset;
1857                                 e.socket_async_result.Size = e.Count;
1858
1859                                 QueueIOSelectorJob (WriteSem, e.socket_async_result.Handle, new IOSelectorJob (IOOperation.Write, s => BeginSendCallback ((SocketAsyncResult) s, 0), e.socket_async_result));
1860                         }
1861
1862                         return true;
1863                 }
1864
1865                 static AsyncCallback SendAsyncCallback = new AsyncCallback (ares => {
1866                         SocketAsyncEventArgs e = (SocketAsyncEventArgs) ((SocketAsyncResult) ares).AsyncState;
1867
1868                         if (Interlocked.Exchange (ref e.in_progress, 0) != 1)
1869                                 throw new InvalidOperationException ("No operation in progress");
1870
1871                         try {
1872                                 e.BytesTransferred = e.current_socket.EndSend (ares);
1873                         } catch (SocketException se){
1874                                 e.SocketError = se.SocketErrorCode;
1875                         } catch (ObjectDisposedException) {
1876                                 e.SocketError = SocketError.OperationAborted;
1877                         } finally {
1878                                 e.Complete ();
1879                         }
1880                 });
1881
1882                 public IAsyncResult BeginSend (byte[] buffer, int offset, int size, SocketFlags socketFlags, out SocketError errorCode, AsyncCallback callback, object state)
1883                 {
1884                         ThrowIfDisposedAndClosed ();
1885                         ThrowIfBufferNull (buffer);
1886                         ThrowIfBufferOutOfRange (buffer, offset, size);
1887
1888                         if (!is_connected) {
1889                                 errorCode = SocketError.NotConnected;
1890                                 return null;
1891                         }
1892
1893                         errorCode = SocketError.Success;
1894
1895                         SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.Send) {
1896                                 Buffer = buffer,
1897                                 Offset = offset,
1898                                 Size = size,
1899                                 SockFlags = socketFlags,
1900                         };
1901
1902                         QueueIOSelectorJob (WriteSem, sockares.Handle, new IOSelectorJob (IOOperation.Write, s => BeginSendCallback ((SocketAsyncResult) s, 0), sockares));
1903
1904                         return sockares;
1905                 }
1906
1907                 static void BeginSendCallback (SocketAsyncResult sockares, int sent_so_far)
1908                 {
1909                         int total = 0;
1910
1911                         try {
1912                                 total = Socket.Send_internal (sockares.socket.m_Handle, sockares.Buffer, sockares.Offset, sockares.Size, sockares.SockFlags, out sockares.error, false);
1913                         } catch (Exception e) {
1914                                 sockares.Complete (e);
1915                                 return;
1916                         }
1917
1918                         if (sockares.error == 0) {
1919                                 sent_so_far += total;
1920                                 sockares.Offset += total;
1921                                 sockares.Size -= total;
1922
1923                                 if (sockares.socket.CleanedUp) {
1924                                         sockares.Complete (total);
1925                                         return;
1926                                 }
1927
1928                                 if (sockares.Size > 0) {
1929                                         IOSelector.Add (sockares.Handle, new IOSelectorJob (IOOperation.Write, s => BeginSendCallback ((SocketAsyncResult) s, sent_so_far), sockares));
1930                                         return; // Have to finish writing everything. See bug #74475.
1931                                 }
1932
1933                                 sockares.Total = sent_so_far;
1934                         }
1935
1936                         sockares.Complete (total);
1937                 }
1938
1939                 [CLSCompliant (false)]
1940                 public IAsyncResult BeginSend (IList<ArraySegment<byte>> buffers, SocketFlags socketFlags, out SocketError errorCode, AsyncCallback callback, object state)
1941                 {
1942                         ThrowIfDisposedAndClosed ();
1943
1944                         if (buffers == null)
1945                                 throw new ArgumentNullException ("buffers");
1946
1947                         if (!is_connected) {
1948                                 errorCode = SocketError.NotConnected;
1949                                 return null;
1950                         }
1951
1952                         errorCode = SocketError.Success;
1953
1954                         SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.SendGeneric) {
1955                                 Buffers = buffers,
1956                                 SockFlags = socketFlags,
1957                         };
1958
1959                         QueueIOSelectorJob (WriteSem, sockares.Handle, new IOSelectorJob (IOOperation.Write, BeginSendGenericCallback, sockares));
1960
1961                         return sockares;
1962                 }
1963
1964                 static IOAsyncCallback BeginSendGenericCallback = new IOAsyncCallback (ares => {
1965                         SocketAsyncResult sockares = (SocketAsyncResult) ares;
1966                         int total = 0;
1967
1968                         try {
1969                                 total = sockares.socket.Send (sockares.Buffers, sockares.SockFlags);
1970                         } catch (Exception e) {
1971                                 sockares.Complete (e);
1972                                 return;
1973                         }
1974
1975                         sockares.Complete (total);
1976                 });
1977
1978                 public int EndSend (IAsyncResult asyncResult, out SocketError errorCode)
1979                 {
1980                         ThrowIfDisposedAndClosed ();
1981
1982                         SocketAsyncResult sockares = ValidateEndIAsyncResult (asyncResult, "EndSend", "asyncResult");
1983
1984                         if (!sockares.IsCompleted)
1985                                 sockares.AsyncWaitHandle.WaitOne ();
1986
1987                         errorCode = sockares.ErrorCode;
1988
1989                         if (errorCode != SocketError.Success && errorCode != SocketError.WouldBlock && errorCode != SocketError.InProgress)
1990                                 is_connected = false;
1991
1992                         /* If no socket error occurred, call CheckIfThrowDelayedException in
1993                          * case there are other kinds of exceptions that should be thrown.*/
1994                         if (errorCode == SocketError.Success)
1995                                 sockares.CheckIfThrowDelayedException ();
1996
1997                         return sockares.Total;
1998                 }
1999
2000                 static int Send_internal (SafeSocketHandle safeHandle, WSABUF[] bufarray, SocketFlags flags, out int error, bool blocking)
2001                 {
2002                         try {
2003                                 safeHandle.RegisterForBlockingSyscall ();
2004                                 return Send_internal (safeHandle.DangerousGetHandle (), bufarray, flags, out error, blocking);
2005                         } finally {
2006                                 safeHandle.UnRegisterForBlockingSyscall ();
2007                         }
2008                 }
2009
2010                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2011                 extern static int Send_internal (IntPtr sock, WSABUF[] bufarray, SocketFlags flags, out int error, bool blocking);
2012
2013                 static int Send_internal (SafeSocketHandle safeHandle, byte[] buf, int offset, int count, SocketFlags flags, out int error, bool blocking)
2014                 {
2015                         try {
2016                                 safeHandle.RegisterForBlockingSyscall ();
2017                                 return Send_internal (safeHandle.DangerousGetHandle (), buf, offset, count, flags, out error, blocking);
2018                         } finally {
2019                                 safeHandle.UnRegisterForBlockingSyscall ();
2020                         }
2021                 }
2022
2023                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
2024                 extern static int Send_internal(IntPtr sock, byte[] buf, int offset, int count, SocketFlags flags, out int error, bool blocking);
2025
2026 #endregion
2027
2028 #region SendTo
2029
2030                 public int SendTo (byte [] buffer, int offset, int size, SocketFlags socketFlags, EndPoint remoteEP)
2031                 {
2032                         ThrowIfDisposedAndClosed ();
2033                         ThrowIfBufferNull (buffer);
2034                         ThrowIfBufferOutOfRange (buffer, offset, size);
2035
2036                         if (remoteEP == null)
2037                                 throw new ArgumentNullException("remoteEP");
2038
2039                         int error;
2040                         int ret = SendTo_internal (m_Handle, buffer, offset, size, socketFlags, remoteEP.Serialize (), out error, is_blocking);
2041
2042                         SocketError err = (SocketError) error;
2043                         if (err != 0) {
2044                                 if (err != SocketError.WouldBlock && err != SocketError.InProgress)
2045                                         is_connected = false;
2046                                 throw new SocketException (error);
2047                         }
2048
2049                         is_connected = true;
2050                         is_bound = true;
2051                         seed_endpoint = remoteEP;
2052
2053                         return ret;
2054                 }
2055
2056                 public bool SendToAsync (SocketAsyncEventArgs e)
2057                 {
2058                         // NO check is made whether e != null in MS.NET (NRE is thrown in such case)
2059
2060                         ThrowIfDisposedAndClosed ();
2061
2062                         if (e.BufferList != null)
2063                                 throw new NotSupportedException ("Mono doesn't support using BufferList at this point.");
2064                         if (e.RemoteEndPoint == null)
2065                                 throw new ArgumentNullException ("remoteEP", "Value cannot be null.");
2066
2067                         InitSocketAsyncEventArgs (e, SendToAsyncCallback, e, SocketOperation.SendTo);
2068
2069                         e.socket_async_result.Buffer = e.Buffer;
2070                         e.socket_async_result.Offset = e.Offset;
2071                         e.socket_async_result.Size = e.Count;
2072                         e.socket_async_result.SockFlags = e.SocketFlags;
2073                         e.socket_async_result.EndPoint = e.RemoteEndPoint;
2074
2075                         QueueIOSelectorJob (WriteSem, e.socket_async_result.Handle, new IOSelectorJob (IOOperation.Write, s => BeginSendToCallback ((SocketAsyncResult) s, 0), e.socket_async_result));
2076
2077                         return true;
2078                 }
2079
2080                 static AsyncCallback SendToAsyncCallback = new AsyncCallback (ares => {
2081                         SocketAsyncEventArgs e = (SocketAsyncEventArgs) ((SocketAsyncResult) ares).AsyncState;
2082
2083                         if (Interlocked.Exchange (ref e.in_progress, 0) != 1)
2084                                 throw new InvalidOperationException ("No operation in progress");
2085
2086                         try {
2087                                 e.BytesTransferred = e.current_socket.EndSendTo (ares);
2088                         } catch (SocketException ex) {
2089                                 e.SocketError = ex.SocketErrorCode;
2090                         } catch (ObjectDisposedException) {
2091                                 e.SocketError = SocketError.OperationAborted;
2092                         } finally {
2093                                 e.Complete ();
2094                         }
2095                 });
2096
2097                 public IAsyncResult BeginSendTo(byte[] buffer, int offset, int size, SocketFlags socketFlags, EndPoint remoteEP, AsyncCallback callback, object state)
2098                 {
2099                         ThrowIfDisposedAndClosed ();
2100                         ThrowIfBufferNull (buffer);
2101                         ThrowIfBufferOutOfRange (buffer, offset, size);
2102
2103                         SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.SendTo) {
2104                                 Buffer = buffer,
2105                                 Offset = offset,
2106                                 Size = size,
2107                                 SockFlags = socketFlags,
2108                                 EndPoint = remoteEP,
2109                         };
2110
2111                         QueueIOSelectorJob (WriteSem, sockares.Handle, new IOSelectorJob (IOOperation.Write, s => BeginSendToCallback ((SocketAsyncResult) s, 0), sockares));
2112
2113                         return sockares;
2114                 }
2115
2116                 static void BeginSendToCallback (SocketAsyncResult sockares, int sent_so_far)
2117                 {
2118                         int total = 0;
2119                         try {
2120                                 total = sockares.socket.SendTo (sockares.Buffer, sockares.Offset, sockares.Size, sockares.SockFlags, sockares.EndPoint);
2121
2122                                 if (sockares.error == 0) {
2123                                         sent_so_far += total;
2124                                         sockares.Offset += total;
2125                                         sockares.Size -= total;
2126                                 }
2127
2128                                 if (sockares.Size > 0) {
2129                                         IOSelector.Add (sockares.Handle, new IOSelectorJob (IOOperation.Write, s => BeginSendToCallback ((SocketAsyncResult) s, sent_so_far), sockares));
2130                                         return; // Have to finish writing everything. See bug #74475.
2131                                 }
2132
2133                                 sockares.Total = sent_so_far;
2134                         } catch (Exception e) {
2135                                 sockares.Complete (e);
2136                                 return;
2137                         }
2138
2139                         sockares.Complete ();
2140                 }
2141
2142                 public int EndSendTo (IAsyncResult asyncResult)
2143                 {
2144                         ThrowIfDisposedAndClosed ();
2145
2146                         SocketAsyncResult sockares = ValidateEndIAsyncResult (asyncResult, "EndSendTo", "result");
2147
2148                         if (!sockares.IsCompleted)
2149                                 sockares.AsyncWaitHandle.WaitOne();
2150
2151                         sockares.CheckIfThrowDelayedException();
2152
2153                         return sockares.Total;
2154                 }
2155
2156                 static int SendTo_internal (SafeSocketHandle safeHandle, byte[] buffer, int offset, int count, SocketFlags flags, SocketAddress sa, out int error, bool blocking)
2157                 {
2158                         try {
2159                                 safeHandle.RegisterForBlockingSyscall ();
2160                                 return SendTo_internal (safeHandle.DangerousGetHandle (), buffer, offset, count, flags, sa, out error, blocking);
2161                         } finally {
2162                                 safeHandle.UnRegisterForBlockingSyscall ();
2163                         }
2164                 }
2165
2166                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
2167                 extern static int SendTo_internal (IntPtr sock, byte[] buffer, int offset, int count, SocketFlags flags, SocketAddress sa, out int error, bool blocking);
2168
2169 #endregion
2170
2171 #region SendFile
2172
2173                 public void SendFile (string fileName, byte[] preBuffer, byte[] postBuffer, TransmitFileOptions flags)
2174                 {
2175                         ThrowIfDisposedAndClosed ();
2176
2177                         if (!is_connected)
2178                                 throw new NotSupportedException ();
2179                         if (!is_blocking)
2180                                 throw new InvalidOperationException ();
2181
2182                         int error = 0;
2183                         if (!SendFile_internal (m_Handle, fileName, preBuffer, postBuffer, flags, out error, is_blocking) || error != 0) {
2184                                 SocketException exc = new SocketException (error);
2185                                 if (exc.ErrorCode == 2 || exc.ErrorCode == 3)
2186                                         throw new FileNotFoundException ();
2187                                 throw exc;
2188                         }
2189                 }
2190
2191                 public IAsyncResult BeginSendFile (string fileName, byte[] preBuffer, byte[] postBuffer, TransmitFileOptions flags, AsyncCallback callback, object state)
2192                 {
2193                         ThrowIfDisposedAndClosed ();
2194
2195                         if (!is_connected)
2196                                 throw new NotSupportedException ();
2197                         if (!File.Exists (fileName))
2198                                 throw new FileNotFoundException ();
2199
2200                         SendFileHandler handler = new SendFileHandler (SendFile);
2201
2202                         return new SendFileAsyncResult (handler, handler.BeginInvoke (fileName, preBuffer, postBuffer, flags, ar => callback (new SendFileAsyncResult (handler, ar)), state));
2203                 }
2204
2205                 public void EndSendFile (IAsyncResult asyncResult)
2206                 {
2207                         ThrowIfDisposedAndClosed ();
2208
2209                         if (asyncResult == null)
2210                                 throw new ArgumentNullException ("asyncResult");
2211
2212                         SendFileAsyncResult ares = asyncResult as SendFileAsyncResult;
2213                         if (ares == null)
2214                                 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
2215
2216                         ares.Delegate.EndInvoke (ares.Original);
2217                 }
2218
2219                 static bool SendFile_internal (SafeSocketHandle safeHandle, string filename, byte [] pre_buffer, byte [] post_buffer, TransmitFileOptions flags, out int error, bool blocking)
2220                 {
2221                         try {
2222                                 safeHandle.RegisterForBlockingSyscall ();
2223                                 return SendFile_internal (safeHandle.DangerousGetHandle (), filename, pre_buffer, post_buffer, flags, out error, blocking);
2224                         } finally {
2225                                 safeHandle.UnRegisterForBlockingSyscall ();
2226                         }
2227                 }
2228
2229                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
2230                 extern static bool SendFile_internal (IntPtr sock, string filename, byte [] pre_buffer, byte [] post_buffer, TransmitFileOptions flags, out int error, bool blocking);
2231
2232                 delegate void SendFileHandler (string fileName, byte [] preBuffer, byte [] postBuffer, TransmitFileOptions flags);
2233
2234                 sealed class SendFileAsyncResult : IAsyncResult {
2235                         IAsyncResult ares;
2236                         SendFileHandler d;
2237
2238                         public SendFileAsyncResult (SendFileHandler d, IAsyncResult ares)
2239                         {
2240                                 this.d = d;
2241                                 this.ares = ares;
2242                         }
2243
2244                         public object AsyncState {
2245                                 get { return ares.AsyncState; }
2246                         }
2247
2248                         public WaitHandle AsyncWaitHandle {
2249                                 get { return ares.AsyncWaitHandle; }
2250                         }
2251
2252                         public bool CompletedSynchronously {
2253                                 get { return ares.CompletedSynchronously; }
2254                         }
2255
2256                         public bool IsCompleted {
2257                                 get { return ares.IsCompleted; }
2258                         }
2259
2260                         public SendFileHandler Delegate {
2261                                 get { return d; }
2262                         }
2263
2264                         public IAsyncResult Original {
2265                                 get { return ares; }
2266                         }
2267                 }
2268
2269 #endregion
2270
2271 #region SendPackets
2272
2273                 [MonoTODO ("Not implemented")]
2274                 public bool SendPacketsAsync (SocketAsyncEventArgs e)
2275                 {
2276                         // NO check is made whether e != null in MS.NET (NRE is thrown in such case)
2277
2278                         ThrowIfDisposedAndClosed ();
2279
2280                         throw new NotImplementedException ();
2281                 }
2282
2283 #endregion
2284
2285 #region DuplicateAndClose
2286
2287                 [MonoLimitation ("We do not support passing sockets across processes, we merely allow this API to pass the socket across AppDomains")]
2288                 public SocketInformation DuplicateAndClose (int targetProcessId)
2289                 {
2290                         var si = new SocketInformation ();
2291                         si.Options =
2292                                 (is_listening      ? SocketInformationOptions.Listening : 0) |
2293                                 (is_connected      ? SocketInformationOptions.Connected : 0) |
2294                                 (is_blocking       ? 0 : SocketInformationOptions.NonBlocking) |
2295                                 (useOverlappedIO ? SocketInformationOptions.UseOnlyOverlappedIO : 0);
2296
2297                         MonoIOError error;
2298                         IntPtr duplicateHandle;
2299                         if (!MonoIO.DuplicateHandle (System.Diagnostics.Process.GetCurrentProcess ().Handle, Handle, new IntPtr (targetProcessId), out duplicateHandle, 0, 0, 0x00000002 /* DUPLICATE_SAME_ACCESS */, out error))
2300                                 throw MonoIO.GetException (error);
2301
2302                         si.ProtocolInformation = Mono.DataConverter.Pack ("iiiil", (int)addressFamily, (int)socketType, (int)protocolType, is_bound ? 1 : 0, (long)duplicateHandle);
2303                         m_Handle = null;
2304  
2305                         return si;
2306                 }
2307
2308 #endregion
2309
2310 #region GetSocketOption
2311
2312                 public void GetSocketOption (SocketOptionLevel optionLevel, SocketOptionName optionName, byte [] optionValue)
2313                 {
2314                         ThrowIfDisposedAndClosed ();
2315
2316                         if (optionValue == null)
2317                                 throw new SocketException ((int) SocketError.Fault, "Error trying to dereference an invalid pointer");
2318
2319                         int error;
2320                         GetSocketOption_arr_internal (m_Handle, optionLevel, optionName, ref optionValue, out error);
2321
2322                         if (error != 0)
2323                                 throw new SocketException (error);
2324                 }
2325
2326                 public byte [] GetSocketOption (SocketOptionLevel optionLevel, SocketOptionName optionName, int optionLength)
2327                 {
2328                         ThrowIfDisposedAndClosed ();
2329
2330                         int error;
2331                         byte[] byte_val = new byte [optionLength];
2332                         GetSocketOption_arr_internal (m_Handle, optionLevel, optionName, ref byte_val, out error);
2333
2334                         if (error != 0)
2335                                 throw new SocketException (error);
2336
2337                         return byte_val;
2338                 }
2339
2340                 public object GetSocketOption (SocketOptionLevel optionLevel, SocketOptionName optionName)
2341                 {
2342                         ThrowIfDisposedAndClosed ();
2343
2344                         int error;
2345                         object obj_val;
2346                         GetSocketOption_obj_internal (m_Handle, optionLevel, optionName, out obj_val, out error);
2347
2348                         if (error != 0)
2349                                 throw new SocketException (error);
2350
2351                         if (optionName == SocketOptionName.Linger)
2352                                 return (LingerOption) obj_val;
2353                         else if (optionName == SocketOptionName.AddMembership || optionName == SocketOptionName.DropMembership)
2354                                 return (MulticastOption) obj_val;
2355                         else if (obj_val is int)
2356                                 return (int) obj_val;
2357                         else
2358                                 return obj_val;
2359                 }
2360
2361                 static void GetSocketOption_arr_internal (SafeSocketHandle safeHandle, SocketOptionLevel level, SocketOptionName name, ref byte[] byte_val, out int error)
2362                 {
2363                         bool release = false;
2364                         try {
2365                                 safeHandle.DangerousAddRef (ref release);
2366                                 GetSocketOption_arr_internal (safeHandle.DangerousGetHandle (), level, name, ref byte_val, out error);
2367                         } finally {
2368                                 if (release)
2369                                         safeHandle.DangerousRelease ();
2370                         }
2371                 }
2372
2373                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
2374                 extern static void GetSocketOption_arr_internal(IntPtr socket, SocketOptionLevel level, SocketOptionName name, ref byte[] byte_val, out int error);
2375
2376                 static void GetSocketOption_obj_internal (SafeSocketHandle safeHandle, SocketOptionLevel level, SocketOptionName name, out object obj_val, out int error)
2377                 {
2378                         bool release = false;
2379                         try {
2380                                 safeHandle.DangerousAddRef (ref release);
2381                                 GetSocketOption_obj_internal (safeHandle.DangerousGetHandle (), level, name, out obj_val, out error);
2382                         } finally {
2383                                 if (release)
2384                                         safeHandle.DangerousRelease ();
2385                         }
2386                 }
2387
2388                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
2389                 extern static void GetSocketOption_obj_internal(IntPtr socket, SocketOptionLevel level, SocketOptionName name, out object obj_val, out int error);
2390
2391 #endregion
2392
2393 #region SetSocketOption
2394
2395                 public void SetSocketOption (SocketOptionLevel optionLevel, SocketOptionName optionName, byte [] optionValue)
2396                 {
2397                         ThrowIfDisposedAndClosed ();
2398
2399                         // I'd throw an ArgumentNullException, but this is what MS does.
2400                         if (optionValue == null)
2401                                 throw new SocketException ((int) SocketError.Fault, "Error trying to dereference an invalid pointer");
2402
2403                         int error;
2404                         SetSocketOption_internal (m_Handle, optionLevel, optionName, null, optionValue, 0, out error);
2405
2406                         if (error != 0) {
2407                                 if (error == (int) SocketError.InvalidArgument)
2408                                         throw new ArgumentException ();
2409                                 throw new SocketException (error);
2410                         }
2411                 }
2412
2413                 public void SetSocketOption (SocketOptionLevel optionLevel, SocketOptionName optionName, object optionValue)
2414                 {
2415                         ThrowIfDisposedAndClosed ();
2416
2417                         // NOTE: if a null is passed, the byte[] overload is used instead...
2418                         if (optionValue == null)
2419                                 throw new ArgumentNullException("optionValue");
2420
2421                         int error;
2422
2423                         if (optionLevel == SocketOptionLevel.Socket && optionName == SocketOptionName.Linger) {
2424                                 LingerOption linger = optionValue as LingerOption;
2425                                 if (linger == null)
2426                                         throw new ArgumentException ("A 'LingerOption' value must be specified.", "optionValue");
2427                                 SetSocketOption_internal (m_Handle, optionLevel, optionName, linger, null, 0, out error);
2428                         } else if (optionLevel == SocketOptionLevel.IP && (optionName == SocketOptionName.AddMembership || optionName == SocketOptionName.DropMembership)) {
2429                                 MulticastOption multicast = optionValue as MulticastOption;
2430                                 if (multicast == null)
2431                                         throw new ArgumentException ("A 'MulticastOption' value must be specified.", "optionValue");
2432                                 SetSocketOption_internal (m_Handle, optionLevel, optionName, multicast, null, 0, out error);
2433                         } else if (optionLevel == SocketOptionLevel.IPv6 && (optionName == SocketOptionName.AddMembership || optionName == SocketOptionName.DropMembership)) {
2434                                 IPv6MulticastOption multicast = optionValue as IPv6MulticastOption;
2435                                 if (multicast == null)
2436                                         throw new ArgumentException ("A 'IPv6MulticastOption' value must be specified.", "optionValue");
2437                                 SetSocketOption_internal (m_Handle, optionLevel, optionName, multicast, null, 0, out error);
2438                         } else {
2439                                 throw new ArgumentException ("Invalid value specified.", "optionValue");
2440                         }
2441
2442                         if (error != 0) {
2443                                 if (error == (int) SocketError.InvalidArgument)
2444                                         throw new ArgumentException ();
2445                                 throw new SocketException (error);
2446                         }
2447                 }
2448
2449                 public void SetSocketOption (SocketOptionLevel optionLevel, SocketOptionName optionName, bool optionValue)
2450                 {
2451                         int int_val = optionValue ? 1 : 0;
2452
2453                         SetSocketOption (optionLevel, optionName, int_val);
2454                 }
2455
2456                 public void SetSocketOption (SocketOptionLevel optionLevel, SocketOptionName optionName, int optionValue)
2457                 {
2458                         ThrowIfDisposedAndClosed ();
2459
2460                         int error;
2461                         SetSocketOption_internal (m_Handle, optionLevel, optionName, null, null, optionValue, out error);
2462
2463                         if (error != 0) {
2464                                 if (error == (int) SocketError.InvalidArgument)
2465                                         throw new ArgumentException ();
2466                                 throw new SocketException (error);
2467                         }
2468                 }
2469
2470                 static void SetSocketOption_internal (SafeSocketHandle safeHandle, SocketOptionLevel level, SocketOptionName name, object obj_val, byte [] byte_val, int int_val, out int error)
2471                 {
2472                         bool release = false;
2473                         try {
2474                                 safeHandle.DangerousAddRef (ref release);
2475                                 SetSocketOption_internal (safeHandle.DangerousGetHandle (), level, name, obj_val, byte_val, int_val, out error);
2476                         } finally {
2477                                 if (release)
2478                                         safeHandle.DangerousRelease ();
2479                         }
2480                 }
2481
2482                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
2483                 extern static void SetSocketOption_internal (IntPtr socket, SocketOptionLevel level, SocketOptionName name, object obj_val, byte [] byte_val, int int_val, out int error);
2484
2485 #endregion
2486
2487 #region IOControl
2488
2489                 public int IOControl (int ioControlCode, byte [] optionInValue, byte [] optionOutValue)
2490                 {
2491                         if (CleanedUp)
2492                                 throw new ObjectDisposedException (GetType ().ToString ());
2493
2494                         int error;
2495                         int result = IOControl_internal (m_Handle, ioControlCode, optionInValue, optionOutValue, out error);
2496
2497                         if (error != 0)
2498                                 throw new SocketException (error);
2499                         if (result == -1)
2500                                 throw new InvalidOperationException ("Must use Blocking property instead.");
2501
2502                         return result;
2503                 }
2504
2505                 static int IOControl_internal (SafeSocketHandle safeHandle, int ioctl_code, byte [] input, byte [] output, out int error)
2506                 {
2507                         bool release = false;
2508                         try {
2509                                 safeHandle.DangerousAddRef (ref release);
2510                                 return IOControl_internal (safeHandle.DangerousGetHandle (), ioctl_code, input, output, out error);
2511                         } finally {
2512                                 if (release)
2513                                         safeHandle.DangerousRelease ();
2514                         }
2515                 }
2516
2517                 /* See Socket.IOControl, WSAIoctl documentation in MSDN. The common options between UNIX
2518                  * and Winsock are FIONREAD, FIONBIO and SIOCATMARK. Anything else will depend on the system
2519                  * except SIO_KEEPALIVE_VALS which is properly handled on both windows and linux. */
2520                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
2521                 extern static int IOControl_internal (IntPtr sock, int ioctl_code, byte [] input, byte [] output, out int error);
2522
2523 #endregion
2524
2525 #region Close
2526
2527                 public void Close ()
2528                 {
2529                         linger_timeout = 0;
2530                         Dispose ();
2531                 }
2532
2533                 public void Close (int timeout)
2534                 {
2535                         linger_timeout = timeout;
2536                         Dispose ();
2537                 }
2538
2539                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
2540                 internal extern static void Close_internal (IntPtr socket, out int error);
2541
2542 #endregion
2543
2544 #region Shutdown
2545
2546                 public void Shutdown (SocketShutdown how)
2547                 {
2548                         ThrowIfDisposedAndClosed ();
2549
2550                         if (!is_connected)
2551                                 throw new SocketException (10057); // Not connected
2552
2553                         int error;
2554                         Shutdown_internal (m_Handle, how, out error);
2555
2556                         if (error != 0)
2557                                 throw new SocketException (error);
2558                 }
2559
2560                 static void Shutdown_internal (SafeSocketHandle safeHandle, SocketShutdown how, out int error)
2561                 {
2562                         bool release = false;
2563                         try {
2564                                 safeHandle.DangerousAddRef (ref release);
2565                                 Shutdown_internal (safeHandle.DangerousGetHandle (), how, out error);
2566                         } finally {
2567                                 if (release)
2568                                         safeHandle.DangerousRelease ();
2569                         }
2570                 }
2571
2572                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2573                 internal extern static void Shutdown_internal (IntPtr socket, SocketShutdown how, out int error);
2574
2575 #endregion
2576
2577 #region Dispose
2578
2579                 protected virtual void Dispose (bool disposing)
2580                 {
2581                         if (CleanedUp)
2582                                 return;
2583
2584                         m_IntCleanedUp = 1;
2585                         bool was_connected = is_connected;
2586                         is_connected = false;
2587
2588                         if (m_Handle != null) {
2589                                 is_closed = true;
2590                                 IntPtr x = Handle;
2591
2592                                 if (was_connected)
2593                                         Linger (x);
2594
2595                                 m_Handle.Dispose ();
2596                         }
2597                 }
2598
2599                 void Linger (IntPtr handle)
2600                 {
2601                         if (!is_connected || linger_timeout <= 0)
2602                                 return;
2603
2604                         /* We don't want to receive any more data */
2605                         int error;
2606                         Shutdown_internal (handle, SocketShutdown.Receive, out error);
2607
2608                         if (error != 0)
2609                                 return;
2610
2611                         int seconds = linger_timeout / 1000;
2612                         int ms = linger_timeout % 1000;
2613                         if (ms > 0) {
2614                                 /* If the other end closes, this will return 'true' with 'Available' == 0 */
2615                                 Poll_internal (handle, SelectMode.SelectRead, ms * 1000, out error);
2616                                 if (error != 0)
2617                                         return;
2618                         }
2619
2620                         if (seconds > 0) {
2621                                 LingerOption linger = new LingerOption (true, seconds);
2622                                 SetSocketOption_internal (handle, SocketOptionLevel.Socket, SocketOptionName.Linger, linger, null, 0, out error);
2623                                 /* Not needed, we're closing upon return */
2624                                 //if (error != 0)
2625                                 //      return;
2626                         }
2627                 }
2628
2629 #endregion
2630
2631                 void ThrowIfDisposedAndClosed (Socket socket)
2632                 {
2633                         if (socket.CleanedUp && socket.is_closed)
2634                                 throw new ObjectDisposedException (socket.GetType ().ToString ());
2635                 }
2636
2637                 void ThrowIfDisposedAndClosed ()
2638                 {
2639                         if (CleanedUp && is_closed)
2640                                 throw new ObjectDisposedException (GetType ().ToString ());
2641                 }
2642
2643                 void ThrowIfBufferNull (byte[] buffer)
2644                 {
2645                         if (buffer == null)
2646                                 throw new ArgumentNullException ("buffer");
2647                 }
2648
2649                 void ThrowIfBufferOutOfRange (byte[] buffer, int offset, int size)
2650                 {
2651                         if (offset < 0)
2652                                 throw new ArgumentOutOfRangeException ("offset", "offset must be >= 0");
2653                         if (offset > buffer.Length)
2654                                 throw new ArgumentOutOfRangeException ("offset", "offset must be <= buffer.Length");
2655                         if (size < 0)
2656                                 throw new ArgumentOutOfRangeException ("size", "size must be >= 0");
2657                         if (size > buffer.Length - offset)
2658                                 throw new ArgumentOutOfRangeException ("size", "size must be <= buffer.Length - offset");
2659                 }
2660
2661                 void ThrowIfUdp ()
2662                 {
2663                         if (protocolType == ProtocolType.Udp)
2664                                 throw new SocketException ((int)SocketError.ProtocolOption);
2665                 }
2666
2667                 SocketAsyncResult ValidateEndIAsyncResult (IAsyncResult ares, string methodName, string argName)
2668                 {
2669                         if (ares == null)
2670                                 throw new ArgumentNullException (argName);
2671
2672                         SocketAsyncResult sockares = ares as SocketAsyncResult;
2673                         if (sockares == null)
2674                                 throw new ArgumentException ("Invalid IAsyncResult", argName);
2675                         if (Interlocked.CompareExchange (ref sockares.EndCalled, 1, 0) == 1)
2676                                 throw new InvalidOperationException (methodName + " can only be called once per asynchronous operation");
2677
2678                         return sockares;
2679                 }
2680
2681                 void QueueIOSelectorJob (SemaphoreSlim sem, IntPtr handle, IOSelectorJob job)
2682                 {
2683                         var task = sem.WaitAsync();
2684                         // fast path without Task<Action> allocation.
2685                         if (task.IsCompleted) {
2686                                 if (CleanedUp) {
2687                                         job.MarkDisposed ();
2688                                         return;
2689                                 }
2690                                 IOSelector.Add (handle, job);
2691                         }
2692                         else
2693                         {
2694                                 task.ContinueWith( t => {
2695                                         if (CleanedUp) {
2696                                                 job.MarkDisposed ();
2697                                                 return;
2698                                         }
2699                                         IOSelector.Add(handle, job);
2700                                 });
2701                         }
2702                 }
2703
2704                 void InitSocketAsyncEventArgs (SocketAsyncEventArgs e, AsyncCallback callback, object state, SocketOperation operation)
2705                 {
2706                         e.socket_async_result.Init (this, callback, state, operation);
2707                         if (e.AcceptSocket != null) {
2708                                 e.socket_async_result.AcceptSocket = e.AcceptSocket;
2709                         }
2710                         e.current_socket = this;
2711                         e.SetLastOperation (SocketOperationToSocketAsyncOperation (operation));
2712                         e.SocketError = SocketError.Success;
2713                         e.BytesTransferred = 0;
2714                 }
2715
2716                 SocketAsyncOperation SocketOperationToSocketAsyncOperation (SocketOperation op)
2717                 {
2718                         switch (op) {
2719                         case SocketOperation.Connect:
2720                                 return SocketAsyncOperation.Connect;
2721                         case SocketOperation.Accept:
2722                                 return SocketAsyncOperation.Accept;
2723                         case SocketOperation.Disconnect:
2724                                 return SocketAsyncOperation.Disconnect;
2725                         case SocketOperation.Receive:
2726                         case SocketOperation.ReceiveGeneric:
2727                                 return SocketAsyncOperation.Receive;
2728                         case SocketOperation.ReceiveFrom:
2729                                 return SocketAsyncOperation.ReceiveFrom;
2730                         case SocketOperation.Send:
2731                         case SocketOperation.SendGeneric:
2732                                 return SocketAsyncOperation.Send;
2733                         case SocketOperation.SendTo:
2734                                 return SocketAsyncOperation.SendTo;
2735                         default:
2736                                 throw new NotImplementedException (String.Format ("Operation {0} is not implemented", op));
2737                         }
2738                 }
2739                 
2740                 IPEndPoint RemapIPEndPoint (IPEndPoint input) {
2741                         // If socket is DualMode ensure we automatically handle mapping IPv4 addresses to IPv6.
2742                         if (IsDualMode && input.AddressFamily == AddressFamily.InterNetwork)
2743                                 return new IPEndPoint (input.Address.MapToIPv6 (), input.Port);
2744                         
2745                         return input;
2746                 }
2747                 
2748                 [StructLayout (LayoutKind.Sequential)]
2749                 struct WSABUF {
2750                         public int len;
2751                         public IntPtr buf;
2752                 }
2753
2754                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
2755                 internal static extern void cancel_blocking_socket_operation (Thread thread);
2756
2757                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
2758                 internal static extern bool SupportsPortReuse (ProtocolType proto);
2759
2760                 internal static int FamilyHint {
2761                         get {
2762                                 // Returns one of
2763                                 //      MONO_HINT_UNSPECIFIED           = 0,
2764                                 //      MONO_HINT_IPV4                          = 1,
2765                                 //      MONO_HINT_IPV6                          = 2,
2766
2767                                 int hint = 0;
2768                                 if (OSSupportsIPv4) {
2769                                         hint = 1;
2770                                 }
2771
2772                                 if (OSSupportsIPv6) {
2773                                         hint = hint == 0 ? 2 : 0;
2774                                 }
2775
2776                                 return hint;
2777                         }
2778                 }
2779
2780                 static bool IsProtocolSupported (NetworkInterfaceComponent networkInterface)
2781                 {
2782 #if MOBILE
2783                         return true;
2784 #else
2785                         var nics = NetworkInterface.GetAllNetworkInterfaces ();
2786                         foreach (var adapter in nics) {
2787                                 if (adapter.Supports (networkInterface))
2788                                         return true;
2789                         }
2790
2791                         return false;
2792 #endif
2793                 }
2794         }
2795 }
2796