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