Merge pull request #3613 from kloun/patch-4
[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                 /*
59                  *      These two fields are looked up by name by the runtime, don't change
60                  *  their name without also updating the runtime code.
61                  */
62                 static int ipv4_supported = -1;
63                 static int ipv6_supported = -1;
64
65                 /* true if we called Close_internal */
66                 bool is_closed;
67
68                 bool is_listening;
69                 bool use_overlapped_io;
70
71                 int linger_timeout;
72
73                 AddressFamily address_family;
74                 SocketType socket_type;
75                 ProtocolType protocol_type;
76
77                 /* the field "safe_handle" is looked up by name by the runtime */
78                 internal SafeSocketHandle safe_handle;
79
80                 /*
81                  * This EndPoint is used when creating new endpoints. Because
82                  * there are many types of EndPoints possible,
83                  * seed_endpoint.Create(addr) is used for creating new ones.
84                  * As such, this value is set on Bind, SentTo, ReceiveFrom,
85                  * Connect, etc.
86                  */
87                 internal EndPoint seed_endpoint = null;
88
89                 internal Queue<KeyValuePair<IntPtr, IOSelectorJob>> readQ = new Queue<KeyValuePair<IntPtr, IOSelectorJob>> (2);
90                 internal Queue<KeyValuePair<IntPtr, IOSelectorJob>> writeQ = new Queue<KeyValuePair<IntPtr, IOSelectorJob>> (2);
91
92                 internal bool is_blocking = true;
93                 internal bool is_bound;
94
95                 /* When true, the socket was connected at the time of the last IO operation */
96                 internal bool is_connected;
97
98                 internal bool is_disposed;
99                 internal bool connect_in_progress;
100
101 #region Constructors
102
103                 static Socket ()
104                 {
105                         if (ipv4_supported == -1) {
106                                 try {
107                                         Socket tmp = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
108                                         tmp.Close();
109
110                                         ipv4_supported = 1;
111                                 } catch {
112                                         ipv4_supported = 0;
113                                 }
114                         }
115
116                         if (ipv6_supported == -1) {
117                                 // We need to put a try/catch around ConfigurationManager methods as will always throw an exception 
118                                 // when run in a mono embedded application.  This occurs as embedded applications do not have a setup
119                                 // for application config.  The exception is not thrown when called from a normal .NET application. 
120                                 //
121                                 // We, then, need to guard calls to the ConfigurationManager.  If the config is not found or throws an
122                                 // exception, will fall through to the existing Socket / API directly below in the code.
123                                 //
124                                 // Also note that catching ConfigurationErrorsException specifically would require library dependency
125                                 // System.Configuration, and wanted to avoid that.
126 #if !MOBILE
127 #if CONFIGURATION_DEP
128                                 try {
129                                         SettingsSection config;
130                                         config = (SettingsSection) System.Configuration.ConfigurationManager.GetSection ("system.net/settings");
131                                         if (config != null)
132                                                 ipv6_supported = config.Ipv6.Enabled ? -1 : 0;
133                                 } catch {
134                                         ipv6_supported = -1;
135                                 }
136 #else
137                                 try {
138                                         NetConfig config = System.Configuration.ConfigurationSettings.GetConfig("system.net/settings") as NetConfig;
139                                         if (config != null)
140                                                 ipv6_supported = config.ipv6Enabled ? -1 : 0;
141                                 } catch {
142                                         ipv6_supported = -1;
143                                 }
144 #endif
145 #endif
146                                 if (ipv6_supported != 0) {
147                                         try {
148                                                 Socket tmp = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp);
149                                                 tmp.Close();
150
151                                                 ipv6_supported = 1;
152                                         } catch {
153                                                 ipv6_supported = 0;
154                                         }
155                                 }
156                         }
157                 }
158
159                 //
160                 // This constructor is used by servers that want to listen for instance on both
161                 // ipv4 and ipv6.   Mono has historically done that if you use InterNetworkV6 (at
162                 // least on Unix), because that is the default behavior unless the IPV6_V6ONLY
163                 // option is explicitly set by using setsockopt (sock, IPPROTO_IPV6, IPV6_ONLY)
164                 //
165                 public Socket (SocketType socketType, ProtocolType protocolType)
166                         : this (AddressFamily.InterNetworkV6, socketType, protocolType)
167                 {
168                         DualMode = true;
169                 }
170                 
171                 public Socket(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType)
172                 {
173                         this.address_family = addressFamily;
174                         this.socket_type = socketType;
175                         this.protocol_type = protocolType;
176
177                         int error;
178                         this.safe_handle = new SafeSocketHandle (Socket_internal (addressFamily, socketType, protocolType, out error), true);
179
180                         if (error != 0)
181                                 throw new SocketException (error);
182
183                         SocketDefaults ();
184                 }
185
186 #if !MOBILE
187                 public Socket (SocketInformation socketInformation)
188                 {
189                         this.is_listening      = (socketInformation.Options & SocketInformationOptions.Listening) != 0;
190                         this.is_connected      = (socketInformation.Options & SocketInformationOptions.Connected) != 0;
191                         this.is_blocking       = (socketInformation.Options & SocketInformationOptions.NonBlocking) == 0;
192                         this.use_overlapped_io = (socketInformation.Options & SocketInformationOptions.UseOnlyOverlappedIO) != 0;
193
194                         var result = Mono.DataConverter.Unpack ("iiiil", socketInformation.ProtocolInformation, 0);
195
196                         this.address_family = (AddressFamily) (int) result [0];
197                         this.socket_type = (SocketType) (int) result [1];
198                         this.protocol_type = (ProtocolType) (int) result [2];
199                         this.is_bound = (ProtocolType) (int) result [3] != 0;
200                         this.safe_handle = new SafeSocketHandle ((IntPtr) (long) result [4], true);
201
202                         SocketDefaults ();
203                 }
204 #endif
205
206                 /* private constructor used by Accept, which already has a socket handle to use */
207                 internal Socket(AddressFamily family, SocketType type, ProtocolType proto, SafeSocketHandle safe_handle)
208                 {
209                         this.address_family = family;
210                         this.socket_type = type;
211                         this.protocol_type = proto;
212                         
213                         this.safe_handle = safe_handle;
214                         this.is_connected = true;
215                 }
216
217                 ~Socket ()
218                 {
219                         Dispose (false);
220                 }
221
222                 void SocketDefaults ()
223                 {
224                         try {
225                                 /* Need to test IPv6 further */
226                                 if (address_family == AddressFamily.InterNetwork
227                                         // || address_family == AddressFamily.InterNetworkV6
228                                 ) {
229                                         /* This is the default, but it probably has nasty side
230                                          * effects on Linux, as the socket option is kludged by
231                                          * turning on or off PMTU discovery... */
232                                         this.DontFragment = false;
233                                         if (protocol_type == ProtocolType.Tcp)
234                                                 this.NoDelay = false;
235                                 } else if (address_family == AddressFamily.InterNetworkV6) {
236                                         this.DualMode = true;
237                                 }
238
239                                 /* Microsoft sets these to 8192, but we are going to keep them
240                                  * both to the OS defaults as these have a big performance impact.
241                                  * on WebClient performance. */
242                                 // this.ReceiveBufferSize = 8192;
243                                 // this.SendBufferSize = 8192;
244                         } catch (SocketException) {
245                         }
246                 }
247
248                 /* Creates a new system socket, returning the handle */
249                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
250                 extern IntPtr Socket_internal (AddressFamily family, SocketType type, ProtocolType proto, out int error);
251
252 #endregion
253
254 #region Properties
255
256                 [ObsoleteAttribute ("Use OSSupportsIPv4 instead")]
257                 public static bool SupportsIPv4 {
258                         get { return ipv4_supported == 1; }
259                 }
260
261                 [ObsoleteAttribute ("Use OSSupportsIPv6 instead")]
262                 public static bool SupportsIPv6 {
263                         get { return ipv6_supported == 1; }
264                 }
265
266 #if MOBILE
267                 public static bool OSSupportsIPv4 {
268                         get { return ipv4_supported == 1; }
269                 }
270 #else
271                 public static bool OSSupportsIPv4 {
272                         get {
273                                 NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces ();
274
275                                 foreach (NetworkInterface adapter in nics) {
276                                         if (adapter.Supports (NetworkInterfaceComponent.IPv4))
277                                                 return true;
278                                 }
279
280                                 return false;
281                         }
282                 }
283 #endif
284
285 #if MOBILE
286                 public static bool OSSupportsIPv6 {
287                         get { return ipv6_supported == 1; }
288                 }
289 #else
290                 public static bool OSSupportsIPv6 {
291                         get {
292                                 NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces ();
293
294                                 foreach (NetworkInterface adapter in nics) {
295                                         if (adapter.Supports (NetworkInterfaceComponent.IPv6))
296                                                 return true;
297                                 }
298
299                                 return false;
300                         }
301                 }
302 #endif
303
304                 public int Available {
305                         get {
306                                 ThrowIfDisposedAndClosed ();
307
308                                 int ret, error;
309                                 ret = Available_internal (safe_handle, out error);
310
311                                 if (error != 0)
312                                         throw new SocketException (error);
313
314                                 return ret;
315                         }
316                 }
317
318                 static int Available_internal (SafeSocketHandle safeHandle, out int error)
319                 {
320                         bool release = false;
321                         try {
322                                 safeHandle.DangerousAddRef (ref release);
323                                 return Available_internal (safeHandle.DangerousGetHandle (), out error);
324                         } finally {
325                                 if (release)
326                                         safeHandle.DangerousRelease ();
327                         }
328                 }
329
330                 /* Returns the amount of data waiting to be read on socket */
331                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
332                 extern static int Available_internal (IntPtr socket, out int error);
333
334                 public bool DontFragment {
335                         get {
336                                 ThrowIfDisposedAndClosed ();
337
338                                 switch (address_family) {
339                                 case AddressFamily.InterNetwork:
340                                         return ((int) GetSocketOption (SocketOptionLevel.IP, SocketOptionName.DontFragment)) != 0;
341                                 case AddressFamily.InterNetworkV6:
342                                         return ((int) GetSocketOption (SocketOptionLevel.IPv6, SocketOptionName.DontFragment)) != 0;
343                                 default:
344                                         throw new NotSupportedException ("This property is only valid for InterNetwork and InterNetworkV6 sockets");
345                                 }
346                         }
347                         set {
348                                 ThrowIfDisposedAndClosed ();
349
350                                 switch (address_family) {
351                                 case AddressFamily.InterNetwork:
352                                         SetSocketOption (SocketOptionLevel.IP, SocketOptionName.DontFragment, value ? 1 : 0);
353                                         break;
354                                 case AddressFamily.InterNetworkV6:
355                                         SetSocketOption (SocketOptionLevel.IPv6, SocketOptionName.DontFragment, value ? 1 : 0);
356                                         break;
357                                 default:
358                                         throw new NotSupportedException ("This property is only valid for InterNetwork and InterNetworkV6 sockets");
359                                 }
360                         }
361                 }
362
363                 public bool EnableBroadcast {
364                         get {
365                                 ThrowIfDisposedAndClosed ();
366
367                                 if (protocol_type != ProtocolType.Udp)
368                                         throw new SocketException ((int) SocketError.ProtocolOption);
369
370                                 return ((int) GetSocketOption (SocketOptionLevel.Socket, SocketOptionName.Broadcast)) != 0;
371                         }
372                         set {
373                                 ThrowIfDisposedAndClosed ();
374
375                                 if (protocol_type != ProtocolType.Udp)
376                                         throw new SocketException ((int) SocketError.ProtocolOption);
377
378                                 SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.Broadcast, value ? 1 : 0);
379                         }
380                 }
381
382                 public bool ExclusiveAddressUse {
383                         get {
384                                 ThrowIfDisposedAndClosed ();
385
386                                 return ((int) GetSocketOption (SocketOptionLevel.Socket, SocketOptionName.ExclusiveAddressUse)) != 0;
387                         }
388                         set {
389                                 ThrowIfDisposedAndClosed ();
390
391                                 if (is_bound)
392                                         throw new InvalidOperationException ("Bind has already been called for this socket");
393
394                                 SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.ExclusiveAddressUse, value ? 1 : 0);
395                         }
396                 }
397
398                 public bool IsBound {
399                         get {
400                                 return is_bound;
401                         }
402                 }
403
404                 public LingerOption LingerState {
405                         get {
406                                 ThrowIfDisposedAndClosed ();
407
408                                 return (LingerOption) GetSocketOption (SocketOptionLevel.Socket, SocketOptionName.Linger);
409                         }
410                         set {
411                                 ThrowIfDisposedAndClosed ();
412                                 SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.Linger, value);
413                         }
414                 }
415
416                 public bool MulticastLoopback {
417                         get {
418                                 ThrowIfDisposedAndClosed ();
419
420                                 /* Even though this option can be set for TCP sockets on Linux, throw
421                                  * this exception anyway to be compatible (the MSDN docs say
422                                  * "Setting this property on a Transmission Control Protocol (TCP)
423                                  * socket will have no effect." but the MS runtime throws the
424                                  * exception...) */
425                                 if (protocol_type == ProtocolType.Tcp)
426                                         throw new SocketException ((int)SocketError.ProtocolOption);
427
428                                 switch (address_family) {
429                                 case AddressFamily.InterNetwork:
430                                         return ((int) GetSocketOption (SocketOptionLevel.IP, SocketOptionName.MulticastLoopback)) != 0;
431                                 case AddressFamily.InterNetworkV6:
432                                         return ((int) GetSocketOption (SocketOptionLevel.IPv6, SocketOptionName.MulticastLoopback)) != 0;
433                                 default:
434                                         throw new NotSupportedException ("This property is only valid for InterNetwork and InterNetworkV6 sockets");
435                                 }
436                         }
437                         set {
438                                 ThrowIfDisposedAndClosed ();
439
440                                 /* Even though this option can be set for TCP sockets on Linux, throw
441                                  * this exception anyway to be compatible (the MSDN docs say
442                                  * "Setting this property on a Transmission Control Protocol (TCP)
443                                  * socket will have no effect." but the MS runtime throws the
444                                  * exception...) */
445                                 if (protocol_type == ProtocolType.Tcp)
446                                         throw new SocketException ((int)SocketError.ProtocolOption);
447
448                                 switch (address_family) {
449                                 case AddressFamily.InterNetwork:
450                                         SetSocketOption (SocketOptionLevel.IP, SocketOptionName.MulticastLoopback, value ? 1 : 0);
451                                         break;
452                                 case AddressFamily.InterNetworkV6:
453                                         SetSocketOption (SocketOptionLevel.IPv6, SocketOptionName.MulticastLoopback, value ? 1 : 0);
454                                         break;
455                                 default:
456                                         throw new NotSupportedException ("This property is only valid for InterNetwork and InterNetworkV6 sockets");
457                                 }
458                         }
459                 }
460
461                 public bool DualMode {
462                         get {
463                                 if (AddressFamily != AddressFamily.InterNetworkV6) 
464                                         throw new NotSupportedException("This protocol version is not supported");
465
466                                 return ((int)GetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only) == 0);
467                         }
468                         set {
469                                 if (AddressFamily != AddressFamily.InterNetworkV6) 
470                                         throw new NotSupportedException("This protocol version is not supported");
471
472                                 SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, value ? 0 : 1);
473                         }
474                 }
475
476                 private bool IsDualMode {
477                         get {
478                                 return AddressFamily == AddressFamily.InterNetworkV6 && DualMode;
479                         }
480                 }
481
482                 [MonoTODO ("This doesn't do anything on Mono yet")]
483                 public bool UseOnlyOverlappedIO {
484                         get { return use_overlapped_io; }
485                         set { use_overlapped_io = value; }
486                 }
487
488                 public IntPtr Handle {
489                         get { return safe_handle.DangerousGetHandle (); }
490                 }
491
492                 // Wish:  support non-IP endpoints.
493                 public EndPoint LocalEndPoint {
494                         get {
495                                 ThrowIfDisposedAndClosed ();
496
497                                 /* If the seed EndPoint is null, Connect, Bind, etc has not yet
498                                  * been called. MS returns null in this case. */
499                                 if (seed_endpoint == null)
500                                         return null;
501
502                                 int error;
503                                 SocketAddress sa = LocalEndPoint_internal (safe_handle, (int) address_family, out error);
504
505                                 if (error != 0)
506                                         throw new SocketException (error);
507
508                                 return seed_endpoint.Create (sa);
509                         }
510                 }
511
512                 static SocketAddress LocalEndPoint_internal (SafeSocketHandle safeHandle, int family, out int error)
513                 {
514                         bool release = false;
515                         try {
516                                 safeHandle.DangerousAddRef (ref release);
517                                 return LocalEndPoint_internal (safeHandle.DangerousGetHandle (), family, out error);
518                         } finally {
519                                 if (release)
520                                         safeHandle.DangerousRelease ();
521                         }
522                 }
523
524                 /* Returns the local endpoint details in addr and port */
525                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
526                 extern static SocketAddress LocalEndPoint_internal (IntPtr socket, int family, out int error);
527
528                 public SocketType SocketType {
529                         get { return socket_type; }
530                 }
531
532                 public int SendTimeout {
533                         get {
534                                 ThrowIfDisposedAndClosed ();
535
536                                 return (int) GetSocketOption (SocketOptionLevel.Socket, SocketOptionName.SendTimeout);
537                         }
538                         set {
539                                 ThrowIfDisposedAndClosed ();
540
541                                 if (value < -1)
542                                         throw new ArgumentOutOfRangeException ("value", "The value specified for a set operation is less than -1");
543
544                                 /* According to the MSDN docs we should adjust values between 1 and
545                                  * 499 to 500, but the MS runtime doesn't do this. */
546                                 if (value == -1)
547                                         value = 0;
548
549                                 SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, value);
550                         }
551                 }
552
553                 public int ReceiveTimeout {
554                         get {
555                                 ThrowIfDisposedAndClosed ();
556
557                                 return (int) GetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout);
558                         }
559                         set {
560                                 ThrowIfDisposedAndClosed ();
561
562                                 if (value < -1)
563                                         throw new ArgumentOutOfRangeException ("value", "The value specified for a set operation is less than -1");
564
565                                 if (value == -1)
566                                         value = 0;
567
568                                 SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, value);
569                         }
570                 }
571
572                 public AddressFamily AddressFamily {
573                         get { return address_family; }
574                 }
575
576                 public bool Blocking {
577                         get { return is_blocking; }
578                         set {
579                                 ThrowIfDisposedAndClosed ();
580
581                                 int error;
582                                 Blocking_internal (safe_handle, value, out error);
583
584                                 if (error != 0)
585                                         throw new SocketException (error);
586
587                                 is_blocking = value;
588                         }
589                 }
590
591                 static void Blocking_internal (SafeSocketHandle safeHandle, bool block, out int error)
592                 {
593                         bool release = false;
594                         try {
595                                 safeHandle.DangerousAddRef (ref release);
596                                 Blocking_internal (safeHandle.DangerousGetHandle (), block, out error);
597                         } finally {
598                                 if (release)
599                                         safeHandle.DangerousRelease ();
600                         }
601                 }
602
603                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
604                 internal extern static void Blocking_internal(IntPtr socket, bool block, out int error);
605
606                 public bool Connected {
607                         get { return is_connected; }
608                         internal set { is_connected = value; }
609                 }
610
611                 public ProtocolType ProtocolType {
612                         get { return protocol_type; }
613                 }
614
615                 public bool NoDelay {
616                         get {
617                                 ThrowIfDisposedAndClosed ();
618                                 ThrowIfUdp ();
619
620                                 return ((int) GetSocketOption (SocketOptionLevel.Tcp, SocketOptionName.NoDelay)) != 0;
621                         }
622
623                         set {
624                                 ThrowIfDisposedAndClosed ();
625                                 ThrowIfUdp ();
626                                 SetSocketOption (SocketOptionLevel.Tcp, SocketOptionName.NoDelay, value ? 1 : 0);
627                         }
628                 }
629
630                 public int ReceiveBufferSize {
631                         get {
632                                 ThrowIfDisposedAndClosed ();
633
634                                 return (int) GetSocketOption (SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer);
635                         }
636                         set {
637                                 ThrowIfDisposedAndClosed ();
638
639                                 if (value < 0)
640                                         throw new ArgumentOutOfRangeException ("value", "The value specified for a set operation is less than zero");
641
642                                 SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer, value);
643                         }
644                 }
645
646                 public int SendBufferSize {
647                         get {
648                                 ThrowIfDisposedAndClosed ();
649
650                                 return (int) GetSocketOption (SocketOptionLevel.Socket, SocketOptionName.SendBuffer);
651                         }
652                         set {
653                                 ThrowIfDisposedAndClosed ();
654
655                                 if (value < 0)
656                                         throw new ArgumentOutOfRangeException ("value", "The value specified for a set operation is less than zero");
657
658                                 SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.SendBuffer, value);
659                         }
660                 }
661
662                 public short Ttl {
663                         get {
664                                 ThrowIfDisposedAndClosed ();
665
666                                 switch (address_family) {
667                                 case AddressFamily.InterNetwork:
668                                         return (short) (int) GetSocketOption (SocketOptionLevel.IP, SocketOptionName.IpTimeToLive);
669                                 case AddressFamily.InterNetworkV6:
670                                         return (short) (int) GetSocketOption (SocketOptionLevel.IPv6, SocketOptionName.HopLimit);
671                                 default:
672                                         throw new NotSupportedException ("This property is only valid for InterNetwork and InterNetworkV6 sockets");
673                                 }
674                         }
675                         set {
676                                 ThrowIfDisposedAndClosed ();
677
678                                 if (value < 0 || value > 255)
679                                         throw new ArgumentOutOfRangeException ("value", "The value specified for a set operation is less than zero or greater than 255.");
680
681                                 switch (address_family) {
682                                 case AddressFamily.InterNetwork:
683                                         SetSocketOption (SocketOptionLevel.IP, SocketOptionName.IpTimeToLive, value);
684                                         break;
685                                 case AddressFamily.InterNetworkV6:
686                                         SetSocketOption (SocketOptionLevel.IPv6, SocketOptionName.HopLimit, value);
687                                         break;
688                                 default:
689                                         throw new NotSupportedException ("This property is only valid for InterNetwork and InterNetworkV6 sockets");
690                                 }
691                         }
692                 }
693
694                 public EndPoint RemoteEndPoint {
695                         get {
696                                 ThrowIfDisposedAndClosed ();
697
698                                 /* If the seed EndPoint is null, Connect, Bind, etc has
699                                  * not yet been called. MS returns null in this case. */
700                                 if (!is_connected || seed_endpoint == null)
701                                         return null;
702
703                                 int error;
704                                 SocketAddress sa = RemoteEndPoint_internal (safe_handle, (int) address_family, out error);
705
706                                 if (error != 0)
707                                         throw new SocketException (error);
708
709                                 return seed_endpoint.Create (sa);
710                         }
711                 }
712
713                 static SocketAddress RemoteEndPoint_internal (SafeSocketHandle safeHandle, int family, out int error)
714                 {
715                         bool release = false;
716                         try {
717                                 safeHandle.DangerousAddRef (ref release);
718                                 return RemoteEndPoint_internal (safeHandle.DangerousGetHandle (), family, out error);
719                         } finally {
720                                 if (release)
721                                         safeHandle.DangerousRelease ();
722                         }
723                 }
724
725                 /* Returns the remote endpoint details in addr and port */
726                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
727                 extern static SocketAddress RemoteEndPoint_internal (IntPtr socket, int family, out int error);
728
729 #endregion
730
731 #region Select
732
733                 public static void Select (IList checkRead, IList checkWrite, IList checkError, int microSeconds)
734                 {
735                         var list = new List<Socket> ();
736                         AddSockets (list, checkRead, "checkRead");
737                         AddSockets (list, checkWrite, "checkWrite");
738                         AddSockets (list, checkError, "checkError");
739
740                         if (list.Count == 3)
741                                 throw new ArgumentNullException ("checkRead, checkWrite, checkError", "All the lists are null or empty.");
742
743                         /* The 'sockets' array contains:
744                          *  - READ socket 0-n, null,
745                          *  - WRITE socket 0-n, null,
746                          *  - ERROR socket 0-n, null */
747                         Socket [] sockets = list.ToArray ();
748
749                         int error;
750                         Select_internal (ref sockets, microSeconds, out error);
751
752                         if (error != 0)
753                                 throw new SocketException (error);
754
755                         if (sockets == null) {
756                                 if (checkRead != null)
757                                         checkRead.Clear ();
758                                 if (checkWrite != null)
759                                         checkWrite.Clear ();
760                                 if (checkError != null)
761                                         checkError.Clear ();
762                                 return;
763                         }
764
765                         int mode = 0;
766                         int count = sockets.Length;
767                         IList currentList = checkRead;
768                         int currentIdx = 0;
769                         for (int i = 0; i < count; i++) {
770                                 Socket sock = sockets [i];
771                                 if (sock == null) { // separator
772                                         if (currentList != null) {
773                                                 // Remove non-signaled sockets after the current one
774                                                 int to_remove = currentList.Count - currentIdx;
775                                                 for (int k = 0; k < to_remove; k++)
776                                                         currentList.RemoveAt (currentIdx);
777                                         }
778                                         currentList = (mode == 0) ? checkWrite : checkError;
779                                         currentIdx = 0;
780                                         mode++;
781                                         continue;
782                                 }
783
784                                 if (mode == 1 && currentList == checkWrite && !sock.is_connected) {
785                                         if ((int) sock.GetSocketOption (SocketOptionLevel.Socket, SocketOptionName.Error) == 0)
786                                                 sock.is_connected = true;
787                                 }
788
789                                 /* Remove non-signaled sockets before the current one */
790                                 while (((Socket) currentList [currentIdx]) != sock)
791                                         currentList.RemoveAt (currentIdx);
792
793                                 currentIdx++;
794                         }
795                 }
796
797                 static void AddSockets (List<Socket> sockets, IList list, string name)
798                 {
799                         if (list != null) {
800                                 foreach (Socket sock in list) {
801                                         if (sock == null) // MS throws a NullRef
802                                                 throw new ArgumentNullException ("name", "Contains a null element");
803                                         sockets.Add (sock);
804                                 }
805                         }
806
807                         sockets.Add (null);
808                 }
809
810                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
811                 extern static void Select_internal (ref Socket [] sockets, int microSeconds, out int error);
812
813 #endregion
814
815 #region Poll
816
817                 public bool Poll (int microSeconds, SelectMode mode)
818                 {
819                         ThrowIfDisposedAndClosed ();
820
821                         if (mode != SelectMode.SelectRead && mode != SelectMode.SelectWrite && mode != SelectMode.SelectError)
822                                 throw new NotSupportedException ("'mode' parameter is not valid.");
823
824                         int error;
825                         bool result = Poll_internal (safe_handle, mode, microSeconds, out error);
826
827                         if (error != 0)
828                                 throw new SocketException (error);
829
830                         if (mode == SelectMode.SelectWrite && result && !is_connected) {
831                                 /* Update the is_connected state; for non-blocking Connect()
832                                  * this is when we can find out that the connect succeeded. */
833                                 if ((int) GetSocketOption (SocketOptionLevel.Socket, SocketOptionName.Error) == 0)
834                                         is_connected = true;
835                         }
836
837                         return result;
838                 }
839
840                 static bool Poll_internal (SafeSocketHandle safeHandle, SelectMode mode, int timeout, out int error)
841                 {
842                         bool release = false;
843                         try {
844                                 safeHandle.DangerousAddRef (ref release);
845                                 return Poll_internal (safeHandle.DangerousGetHandle (), mode, timeout, out error);
846                         } finally {
847                                 if (release)
848                                         safeHandle.DangerousRelease ();
849                         }
850                 }
851
852                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
853                 extern static bool Poll_internal (IntPtr socket, SelectMode mode, int timeout, out int error);
854
855 #endregion
856
857 #region Accept
858
859                 public Socket Accept()
860                 {
861                         ThrowIfDisposedAndClosed ();
862
863                         int error = 0;
864                         SafeSocketHandle safe_handle = Accept_internal (this.safe_handle, out error, is_blocking);
865
866                         if (error != 0) {
867                                 if (is_closed)
868                                         error = SOCKET_CLOSED_CODE;
869                                 throw new SocketException(error);
870                         }
871
872                         Socket accepted = new Socket (this.AddressFamily, this.SocketType, this.ProtocolType, safe_handle) {
873                                 seed_endpoint = this.seed_endpoint,
874                                 Blocking = this.Blocking,
875                         };
876
877                         return accepted;
878                 }
879
880                 internal void Accept (Socket acceptSocket)
881                 {
882                         ThrowIfDisposedAndClosed ();
883
884                         int error = 0;
885                         SafeSocketHandle safe_handle = Accept_internal (this.safe_handle, out error, is_blocking);
886
887                         if (error != 0) {
888                                 if (is_closed)
889                                         error = SOCKET_CLOSED_CODE;
890                                 throw new SocketException (error);
891                         }
892
893                         acceptSocket.address_family = this.AddressFamily;
894                         acceptSocket.socket_type = this.SocketType;
895                         acceptSocket.protocol_type = this.ProtocolType;
896                         acceptSocket.safe_handle = safe_handle;
897                         acceptSocket.is_connected = true;
898                         acceptSocket.seed_endpoint = this.seed_endpoint;
899                         acceptSocket.Blocking = this.Blocking;
900
901                         // FIXME: figure out what if anything else needs to be reset
902                 }
903
904                 public bool AcceptAsync (SocketAsyncEventArgs e)
905                 {
906                         // NO check is made whether e != null in MS.NET (NRE is thrown in such case)
907
908                         ThrowIfDisposedAndClosed ();
909
910                         if (!is_bound)
911                                 throw new InvalidOperationException ("You must call the Bind method before performing this operation.");
912                         if (!is_listening)
913                                 throw new InvalidOperationException ("You must call the Listen method before performing this operation.");
914                         if (e.BufferList != null)
915                                 throw new ArgumentException ("Multiple buffers cannot be used with this method.");
916                         if (e.Count < 0)
917                                 throw new ArgumentOutOfRangeException ("e.Count");
918
919                         Socket acceptSocket = e.AcceptSocket;
920                         if (acceptSocket != null) {
921                                 if (acceptSocket.is_bound || acceptSocket.is_connected)
922                                         throw new InvalidOperationException ("AcceptSocket: The socket must not be bound or connected.");
923                         }
924
925                         InitSocketAsyncEventArgs (e, AcceptAsyncCallback, e, SocketOperation.Accept);
926
927                         QueueIOSelectorJob (readQ, e.socket_async_result.Handle, new IOSelectorJob (IOOperation.Read, BeginAcceptCallback, e.socket_async_result));
928
929                         return true;
930                 }
931
932                 static AsyncCallback AcceptAsyncCallback = new AsyncCallback (ares => {
933                         SocketAsyncEventArgs e = (SocketAsyncEventArgs) ((SocketAsyncResult) ares).AsyncState;
934
935                         if (Interlocked.Exchange (ref e.in_progress, 0) != 1)
936                                 throw new InvalidOperationException ("No operation in progress");
937
938                         try {
939                                 e.AcceptSocket = e.current_socket.EndAccept (ares);
940                         } catch (SocketException ex) {
941                                 e.SocketError = ex.SocketErrorCode;
942                         } catch (ObjectDisposedException) {
943                                 e.SocketError = SocketError.OperationAborted;
944                         } finally {
945                                 if (e.AcceptSocket == null)
946                                         e.AcceptSocket = new Socket (e.current_socket.AddressFamily, e.current_socket.SocketType, e.current_socket.ProtocolType, null);
947                                 e.Complete ();
948                         }
949                 });
950
951                 public IAsyncResult BeginAccept(AsyncCallback callback, object state)
952                 {
953                         ThrowIfDisposedAndClosed ();
954
955                         if (!is_bound || !is_listening)
956                                 throw new InvalidOperationException ();
957
958                         SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.Accept);
959
960                         QueueIOSelectorJob (readQ, sockares.Handle, new IOSelectorJob (IOOperation.Read, BeginAcceptCallback, sockares));
961
962                         return sockares;
963                 }
964
965                 static IOAsyncCallback BeginAcceptCallback = new IOAsyncCallback (ares => {
966                         SocketAsyncResult sockares = (SocketAsyncResult) ares;
967                         Socket acc_socket = null;
968                         try {
969                                 if (sockares.AcceptSocket == null) {
970                                         acc_socket = sockares.socket.Accept ();
971                                 } else {
972                                         acc_socket = sockares.AcceptSocket;
973                                         sockares.socket.Accept (acc_socket);
974                                 }
975
976                         } catch (Exception e) {
977                                 sockares.Complete (e);
978                                 return;
979                         }
980                         sockares.Complete (acc_socket);
981                 });
982
983                 public IAsyncResult BeginAccept (int receiveSize, AsyncCallback callback, object state)
984                 {
985                         ThrowIfDisposedAndClosed ();
986
987                         if (receiveSize < 0)
988                                 throw new ArgumentOutOfRangeException ("receiveSize", "receiveSize is less than zero");
989
990                         SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.AcceptReceive) {
991                                 Buffer = new byte [receiveSize],
992                                 Offset = 0,
993                                 Size = receiveSize,
994                                 SockFlags = SocketFlags.None,
995                         };
996
997                         QueueIOSelectorJob (readQ, sockares.Handle, new IOSelectorJob (IOOperation.Read, BeginAcceptReceiveCallback, sockares));
998
999                         return sockares;
1000                 }
1001
1002                 public IAsyncResult BeginAccept (Socket acceptSocket, int receiveSize, AsyncCallback callback, object state)
1003                 {
1004                         ThrowIfDisposedAndClosed ();
1005
1006                         if (receiveSize < 0)
1007                                 throw new ArgumentOutOfRangeException ("receiveSize", "receiveSize is less than zero");
1008
1009                         if (acceptSocket != null) {
1010                                 ThrowIfDisposedAndClosed (acceptSocket);
1011
1012                                 if (acceptSocket.IsBound)
1013                                         throw new InvalidOperationException ();
1014
1015                                 /* For some reason the MS runtime
1016                                  * barfs if the new socket is not TCP,
1017                                  * even though it's just about to blow
1018                                  * away all those parameters
1019                                  */
1020                                 if (acceptSocket.ProtocolType != ProtocolType.Tcp)
1021                                         throw new SocketException ((int)SocketError.InvalidArgument);
1022                         }
1023
1024                         SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.AcceptReceive) {
1025                                 Buffer = new byte [receiveSize],
1026                                 Offset = 0,
1027                                 Size = receiveSize,
1028                                 SockFlags = SocketFlags.None,
1029                                 AcceptSocket = acceptSocket,
1030                         };
1031
1032                         QueueIOSelectorJob (readQ, sockares.Handle, new IOSelectorJob (IOOperation.Read, BeginAcceptReceiveCallback, sockares));
1033
1034                         return sockares;
1035                 }
1036
1037                 static IOAsyncCallback BeginAcceptReceiveCallback = new IOAsyncCallback (ares => {
1038                         SocketAsyncResult sockares = (SocketAsyncResult) ares;
1039                         Socket acc_socket = null;
1040
1041                         try {
1042                                 if (sockares.AcceptSocket == null) {
1043                                         acc_socket = sockares.socket.Accept ();
1044                                 } else {
1045                                         acc_socket = sockares.AcceptSocket;
1046                                         sockares.socket.Accept (acc_socket);
1047                                 }
1048                         } catch (Exception e) {
1049                                 sockares.Complete (e);
1050                                 return;
1051                         }
1052
1053                         /* It seems the MS runtime special-cases 0-length requested receive data.  See bug 464201. */
1054                         int total = 0;
1055                         if (sockares.Size > 0) {
1056                                 try {
1057                                         SocketError error;
1058                                         total = acc_socket.Receive_nochecks (sockares.Buffer, sockares.Offset, sockares.Size, sockares.SockFlags, out error);
1059                                         if (error != 0) {
1060                                                 sockares.Complete (new SocketException ((int) error));
1061                                                 return;
1062                                         }
1063                                 } catch (Exception e) {
1064                                         sockares.Complete (e);
1065                                         return;
1066                                 }
1067                         }
1068
1069                         sockares.Complete (acc_socket, total);
1070                 });
1071
1072                 public Socket EndAccept (IAsyncResult result)
1073                 {
1074                         int bytes;
1075                         byte[] buffer;
1076                         return EndAccept (out buffer, out bytes, result);
1077                 }
1078
1079                 public Socket EndAccept (out byte[] buffer, IAsyncResult asyncResult)
1080                 {
1081                         int bytes;
1082                         return EndAccept (out buffer, out bytes, asyncResult);
1083                 }
1084
1085                 public Socket EndAccept (out byte[] buffer, out int bytesTransferred, IAsyncResult asyncResult)
1086                 {
1087                         ThrowIfDisposedAndClosed ();
1088
1089                         SocketAsyncResult sockares = ValidateEndIAsyncResult (asyncResult, "EndAccept", "asyncResult");
1090
1091                         if (!sockares.IsCompleted)
1092                                 sockares.AsyncWaitHandle.WaitOne ();
1093
1094                         sockares.CheckIfThrowDelayedException ();
1095
1096                         buffer = sockares.Buffer;
1097                         bytesTransferred = sockares.Total;
1098
1099                         return sockares.AcceptedSocket;
1100                 }
1101
1102                 static SafeSocketHandle Accept_internal (SafeSocketHandle safeHandle, out int error, bool blocking)
1103                 {
1104                         try {
1105                                 safeHandle.RegisterForBlockingSyscall ();
1106                                 var ret = Accept_internal (safeHandle.DangerousGetHandle (), out error, blocking);
1107                                 return new SafeSocketHandle (ret, true);
1108                         } finally {
1109                                 safeHandle.UnRegisterForBlockingSyscall ();
1110                         }
1111                 }
1112
1113                 /* Creates a new system socket, returning the handle */
1114                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1115                 extern static IntPtr Accept_internal (IntPtr sock, out int error, bool blocking);
1116
1117 #endregion
1118
1119 #region Bind
1120
1121                 public void Bind (EndPoint localEP)
1122                 {
1123                         ThrowIfDisposedAndClosed ();
1124
1125                         if (localEP == null)
1126                                 throw new ArgumentNullException("localEP");
1127                                 
1128                         var ipEndPoint = localEP as IPEndPoint;
1129                         if (ipEndPoint != null) {
1130                                 localEP = RemapIPEndPoint (ipEndPoint); 
1131                         }
1132                         
1133                         int error;
1134                         Bind_internal (safe_handle, localEP.Serialize(), out error);
1135
1136                         if (error != 0)
1137                                 throw new SocketException (error);
1138                         if (error == 0)
1139                                 is_bound = true;
1140
1141                         seed_endpoint = localEP;
1142                 }
1143
1144                 private static void Bind_internal (SafeSocketHandle safeHandle, SocketAddress sa, out int error)
1145                 {
1146                         bool release = false;
1147                         try {
1148                                 safeHandle.DangerousAddRef (ref release);
1149                                 Bind_internal (safeHandle.DangerousGetHandle (), sa, out error);
1150                         } finally {
1151                                 if (release)
1152                                         safeHandle.DangerousRelease ();
1153                         }
1154                 }
1155
1156                 // Creates a new system socket, returning the handle
1157                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1158                 private extern static void Bind_internal(IntPtr sock, SocketAddress sa, out int error);
1159
1160 #endregion
1161
1162 #region Listen
1163
1164                 public void Listen (int backlog)
1165                 {
1166                         ThrowIfDisposedAndClosed ();
1167
1168                         if (!is_bound)
1169                                 throw new SocketException ((int) SocketError.InvalidArgument);
1170
1171                         int error;
1172                         Listen_internal(safe_handle, backlog, out error);
1173
1174                         if (error != 0)
1175                                 throw new SocketException (error);
1176
1177                         is_listening = true;
1178                 }
1179
1180                 static void Listen_internal (SafeSocketHandle safeHandle, int backlog, out int error)
1181                 {
1182                         bool release = false;
1183                         try {
1184                                 safeHandle.DangerousAddRef (ref release);
1185                                 Listen_internal (safeHandle.DangerousGetHandle (), backlog, out error);
1186                         } finally {
1187                                 if (release)
1188                                         safeHandle.DangerousRelease ();
1189                         }
1190                 }
1191
1192                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1193                 extern static void Listen_internal (IntPtr sock, int backlog, out int error);
1194
1195 #endregion
1196
1197 #region Connect
1198
1199                 public void Connect (IPAddress address, int port)
1200                 {
1201                         Connect (new IPEndPoint (address, port));
1202                 }
1203
1204                 public void Connect (string host, int port)
1205                 {
1206                         Connect (Dns.GetHostAddresses (host), port);
1207                 }
1208
1209                 public void Connect (IPAddress[] addresses, int port)
1210                 {
1211                         ThrowIfDisposedAndClosed ();
1212
1213                         if (addresses == null)
1214                                 throw new ArgumentNullException ("addresses");
1215                         if (this.AddressFamily != AddressFamily.InterNetwork && this.AddressFamily != AddressFamily.InterNetworkV6)
1216                                 throw new NotSupportedException ("This method is only valid for addresses in the InterNetwork or InterNetworkV6 families");
1217                         if (is_listening)
1218                                 throw new InvalidOperationException ();
1219
1220                         // FIXME: do non-blocking sockets Poll here?
1221                         int error = 0;
1222                         foreach (IPAddress address in addresses) {
1223                                 IPEndPoint iep = new IPEndPoint (address, port);
1224                                 
1225                                 iep = RemapIPEndPoint (iep);
1226
1227                                 Connect_internal (safe_handle, iep.Serialize (), out error);
1228                                 if (error == 0) {
1229                                         is_connected = true;
1230                                         is_bound = true;
1231                                         seed_endpoint = iep;
1232                                         return;
1233                                 }
1234                                 if (error != (int)SocketError.InProgress && error != (int)SocketError.WouldBlock)
1235                                         continue;
1236
1237                                 if (!is_blocking) {
1238                                         Poll (-1, SelectMode.SelectWrite);
1239                                         error = (int)GetSocketOption (SocketOptionLevel.Socket, SocketOptionName.Error);
1240                                         if (error == 0) {
1241                                                 is_connected = true;
1242                                                 is_bound = true;
1243                                                 seed_endpoint = iep;
1244                                                 return;
1245                                         }
1246                                 }
1247                         }
1248
1249                         if (error != 0)
1250                                 throw new SocketException (error);
1251                 }
1252
1253
1254                 public void Connect (EndPoint remoteEP)
1255                 {
1256                         ThrowIfDisposedAndClosed ();
1257
1258                         if (remoteEP == null)
1259                                 throw new ArgumentNullException ("remoteEP");
1260
1261                         IPEndPoint ep = remoteEP as IPEndPoint;
1262                         /* Dgram uses Any to 'disconnect' */
1263                         if (ep != null && socket_type != SocketType.Dgram) {
1264                                 if (ep.Address.Equals (IPAddress.Any) || ep.Address.Equals (IPAddress.IPv6Any))
1265                                         throw new SocketException ((int) SocketError.AddressNotAvailable);
1266                         }
1267
1268                         if (is_listening)
1269                                 throw new InvalidOperationException ();
1270                                 
1271                         if (ep != null) {
1272                                 remoteEP = RemapIPEndPoint (ep);
1273                         }
1274
1275                         SocketAddress serial = remoteEP.Serialize ();
1276
1277                         int error = 0;
1278                         Connect_internal (safe_handle, serial, out error);
1279
1280                         if (error == 0 || error == 10035)
1281                                 seed_endpoint = remoteEP; // Keep the ep around for non-blocking sockets
1282
1283                         if (error != 0) {
1284                                 if (is_closed)
1285                                         error = SOCKET_CLOSED_CODE;
1286                                 throw new SocketException (error);
1287                         }
1288
1289                         is_connected = !(socket_type == SocketType.Dgram && ep != null && (ep.Address.Equals (IPAddress.Any) || ep.Address.Equals (IPAddress.IPv6Any)));
1290                         is_bound = true;
1291                 }
1292
1293                 public bool ConnectAsync (SocketAsyncEventArgs e)
1294                 {
1295                         // NO check is made whether e != null in MS.NET (NRE is thrown in such case)
1296
1297                         ThrowIfDisposedAndClosed ();
1298
1299                         if (is_listening)
1300                                 throw new InvalidOperationException ("You may not perform this operation after calling the Listen method.");
1301                         if (e.RemoteEndPoint == null)
1302                                 throw new ArgumentNullException ("remoteEP");
1303
1304                         InitSocketAsyncEventArgs (e, null, e, SocketOperation.Connect);
1305
1306                         try {
1307                                 IPAddress [] addresses;
1308                                 SocketAsyncResult ares;
1309
1310                                 if (!GetCheckedIPs (e, out addresses)) {
1311                                         e.socket_async_result.EndPoint = e.RemoteEndPoint;
1312                                         ares = (SocketAsyncResult) BeginConnect (e.RemoteEndPoint, ConnectAsyncCallback, e);
1313                                 } else {
1314                                         DnsEndPoint dep = (e.RemoteEndPoint as DnsEndPoint);
1315                                         e.socket_async_result.Addresses = addresses;
1316                                         e.socket_async_result.Port = dep.Port;
1317                                         ares = (SocketAsyncResult) BeginConnect (addresses, dep.Port, ConnectAsyncCallback, e);
1318                                 }
1319
1320                                 if (ares.IsCompleted && ares.CompletedSynchronously) {
1321                                         ares.CheckIfThrowDelayedException ();
1322                                         return false;
1323                                 }
1324                         } catch (Exception exc) {
1325                                 e.socket_async_result.Complete (exc, true);
1326                                 return false;
1327                         }
1328
1329                         return true;
1330                 }
1331
1332                 public static bool ConnectAsync (SocketType socketType, ProtocolType protocolType, SocketAsyncEventArgs e)
1333                 {
1334                         var sock = new Socket (e.RemoteEndPoint.AddressFamily, socketType, protocolType);
1335                         return sock.ConnectAsync (e);
1336                 }
1337
1338                 public static void CancelConnectAsync (SocketAsyncEventArgs e)
1339                 {
1340                         if (e == null)
1341                                 throw new ArgumentNullException("e");
1342
1343                         if (e.in_progress != 0 && e.LastOperation == SocketAsyncOperation.Connect)
1344                                 e.current_socket.Close();
1345                 }
1346
1347                 static AsyncCallback ConnectAsyncCallback = new AsyncCallback (ares => {
1348                         SocketAsyncEventArgs e = (SocketAsyncEventArgs) ((SocketAsyncResult) ares).AsyncState;
1349
1350                         if (Interlocked.Exchange (ref e.in_progress, 0) != 1)
1351                                 throw new InvalidOperationException ("No operation in progress");
1352
1353                         try {
1354                                 e.current_socket.EndConnect (ares);
1355                         } catch (SocketException se) {
1356                                 e.SocketError = se.SocketErrorCode;
1357                         } catch (ObjectDisposedException) {
1358                                 e.SocketError = SocketError.OperationAborted;
1359                         } finally {
1360                                 e.Complete ();
1361                         }
1362                 });
1363
1364                 public IAsyncResult BeginConnect (IPAddress address, int port, AsyncCallback callback, object state)
1365                 {
1366                         ThrowIfDisposedAndClosed ();
1367
1368                         if (address == null)
1369                                 throw new ArgumentNullException ("address");
1370                         if (address.ToString ().Length == 0)
1371                                 throw new ArgumentException ("The length of the IP address is zero");
1372                         if (port <= 0 || port > 65535)
1373                                 throw new ArgumentOutOfRangeException ("port", "Must be > 0 and < 65536");
1374                         if (is_listening)
1375                                 throw new InvalidOperationException ();
1376
1377                         return BeginConnect (new IPEndPoint (address, port), callback, state);
1378                 }
1379
1380                 public IAsyncResult BeginConnect (string host, int port, AsyncCallback callback, object state)
1381                 {
1382                         ThrowIfDisposedAndClosed ();
1383
1384                         if (host == null)
1385                                 throw new ArgumentNullException ("host");
1386                         if (address_family != AddressFamily.InterNetwork && address_family != AddressFamily.InterNetworkV6)
1387                                 throw new NotSupportedException ("This method is valid only for sockets in the InterNetwork and InterNetworkV6 families");
1388                         if (port <= 0 || port > 65535)
1389                                 throw new ArgumentOutOfRangeException ("port", "Must be > 0 and < 65536");
1390                         if (is_listening)
1391                                 throw new InvalidOperationException ();
1392
1393                         return BeginConnect (Dns.GetHostAddresses (host), port, callback, state);
1394                 }
1395
1396                 public IAsyncResult BeginConnect (EndPoint end_point, AsyncCallback callback, object state)
1397                 {
1398                         ThrowIfDisposedAndClosed ();
1399
1400                         if (end_point == null)
1401                                 throw new ArgumentNullException ("end_point");
1402
1403                         SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.Connect) {
1404                                 EndPoint = end_point,
1405                         };
1406
1407                         // Bug #75154: Connect() should not succeed for .Any addresses.
1408                         if (end_point is IPEndPoint) {
1409                                 IPEndPoint ep = (IPEndPoint) end_point;
1410                                 if (ep.Address.Equals (IPAddress.Any) || ep.Address.Equals (IPAddress.IPv6Any)) {
1411                                         sockares.Complete (new SocketException ((int) SocketError.AddressNotAvailable), true);
1412                                         return sockares;
1413                                 }
1414                                 
1415                                 end_point = RemapIPEndPoint (ep);
1416                         }
1417
1418                         int error = 0;
1419
1420                         if (connect_in_progress) {
1421                                 // This could happen when multiple IPs are used
1422                                 // Calling connect() again will reset the connection attempt and cause
1423                                 // an error. Better to just close the socket and move on.
1424                                 connect_in_progress = false;
1425                                 safe_handle.Dispose ();
1426                                 safe_handle = new SafeSocketHandle (Socket_internal (address_family, socket_type, protocol_type, out error), true);
1427                                 if (error != 0)
1428                                         throw new SocketException (error);
1429                         }
1430
1431                         bool blk = is_blocking;
1432                         if (blk)
1433                                 Blocking = false;
1434                         Connect_internal (safe_handle, end_point.Serialize (), out error);
1435                         if (blk)
1436                                 Blocking = true;
1437
1438                         if (error == 0) {
1439                                 // succeeded synch
1440                                 is_connected = true;
1441                                 is_bound = true;
1442                                 sockares.Complete (true);
1443                                 return sockares;
1444                         }
1445
1446                         if (error != (int) SocketError.InProgress && error != (int) SocketError.WouldBlock) {
1447                                 // error synch
1448                                 is_connected = false;
1449                                 is_bound = false;
1450                                 sockares.Complete (new SocketException (error), true);
1451                                 return sockares;
1452                         }
1453
1454                         // continue asynch
1455                         is_connected = false;
1456                         is_bound = false;
1457                         connect_in_progress = true;
1458
1459                         IOSelector.Add (sockares.Handle, new IOSelectorJob (IOOperation.Write, BeginConnectCallback, sockares));
1460
1461                         return sockares;
1462                 }
1463
1464                 public IAsyncResult BeginConnect (IPAddress[] addresses, int port, AsyncCallback callback, object state)
1465                 {
1466                         ThrowIfDisposedAndClosed ();
1467
1468                         if (addresses == null)
1469                                 throw new ArgumentNullException ("addresses");
1470                         if (addresses.Length == 0)
1471                                 throw new ArgumentException ("Empty addresses list");
1472                         if (this.AddressFamily != AddressFamily.InterNetwork && this.AddressFamily != AddressFamily.InterNetworkV6)
1473                                 throw new NotSupportedException ("This method is only valid for addresses in the InterNetwork or InterNetworkV6 families");
1474                         if (port <= 0 || port > 65535)
1475                                 throw new ArgumentOutOfRangeException ("port", "Must be > 0 and < 65536");
1476                         if (is_listening)
1477                                 throw new InvalidOperationException ();
1478
1479                         SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.Connect) {
1480                                 Addresses = addresses,
1481                                 Port = port,
1482                         };
1483
1484                         is_connected = false;
1485
1486                         return BeginMConnect (sockares);
1487                 }
1488
1489                 internal IAsyncResult BeginMConnect (SocketAsyncResult sockares)
1490                 {
1491                         SocketAsyncResult ares = null;
1492                         Exception exc = null;
1493                         AsyncCallback callback;
1494
1495                         for (int i = sockares.CurrentAddress; i < sockares.Addresses.Length; i++) {
1496                                 try {
1497                                         sockares.CurrentAddress++;
1498
1499                                         ares = (SocketAsyncResult) BeginConnect (new IPEndPoint (sockares.Addresses [i], sockares.Port), null, sockares);
1500                                         if (ares.IsCompleted && ares.CompletedSynchronously) {
1501                                                 ares.CheckIfThrowDelayedException ();
1502
1503                                                 callback = ares.AsyncCallback;
1504                                                 if (callback != null)
1505                                                         ThreadPool.UnsafeQueueUserWorkItem (_ => callback (ares), null);
1506                                         }
1507
1508                                         break;
1509                                 } catch (Exception e) {
1510                                         exc = e;
1511                                         ares = null;
1512                                 }
1513                         }
1514
1515                         if (ares == null)
1516                                 throw exc;
1517
1518                         return sockares;
1519                 }
1520
1521                 static IOAsyncCallback BeginConnectCallback = new IOAsyncCallback (ares => {
1522                         SocketAsyncResult sockares = (SocketAsyncResult) ares;
1523
1524                         if (sockares.EndPoint == null) {
1525                                 sockares.Complete (new SocketException ((int)SocketError.AddressNotAvailable));
1526                                 return;
1527                         }
1528
1529                         SocketAsyncResult mconnect = sockares.AsyncState as SocketAsyncResult;
1530                         bool is_mconnect = mconnect != null && mconnect.Addresses != null;
1531
1532                         try {
1533                                 EndPoint ep = sockares.EndPoint;
1534                                 int error_code = (int) sockares.socket.GetSocketOption (SocketOptionLevel.Socket, SocketOptionName.Error);
1535
1536                                 if (error_code == 0) {
1537                                         if (is_mconnect)
1538                                                 sockares = mconnect;
1539
1540                                         sockares.socket.seed_endpoint = ep;
1541                                         sockares.socket.is_connected = true;
1542                                         sockares.socket.is_bound = true;
1543                                         sockares.socket.connect_in_progress = false;
1544                                         sockares.error = 0;
1545                                         sockares.Complete ();
1546                                         return;
1547                                 }
1548
1549                                 if (!is_mconnect) {
1550                                         sockares.socket.connect_in_progress = false;
1551                                         sockares.Complete (new SocketException (error_code));
1552                                         return;
1553                                 }
1554
1555                                 if (mconnect.CurrentAddress >= mconnect.Addresses.Length) {
1556                                         mconnect.Complete (new SocketException (error_code));
1557                                         return;
1558                                 }
1559
1560                                 mconnect.socket.BeginMConnect (mconnect);
1561                         } catch (Exception e) {
1562                                 sockares.socket.connect_in_progress = false;
1563
1564                                 if (is_mconnect)
1565                                         sockares = mconnect;
1566
1567                                 sockares.Complete (e);
1568                                 return;
1569                         }
1570                 });
1571
1572                 public void EndConnect (IAsyncResult result)
1573                 {
1574                         ThrowIfDisposedAndClosed ();
1575
1576                         SocketAsyncResult sockares = ValidateEndIAsyncResult (result, "EndConnect", "result");
1577
1578                         if (!sockares.IsCompleted)
1579                                 sockares.AsyncWaitHandle.WaitOne();
1580
1581                         sockares.CheckIfThrowDelayedException();
1582                 }
1583
1584                 static void Connect_internal (SafeSocketHandle safeHandle, SocketAddress sa, out int error)
1585                 {
1586                         try {
1587                                 safeHandle.RegisterForBlockingSyscall ();
1588                                 Connect_internal (safeHandle.DangerousGetHandle (), sa, out error);
1589                         } finally {
1590                                 safeHandle.UnRegisterForBlockingSyscall ();
1591                         }
1592                 }
1593
1594                 /* Connects to the remote address */
1595                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1596                 extern static void Connect_internal(IntPtr sock, SocketAddress sa, out int error);
1597
1598                 /* Returns :
1599                  *  - false when it is ok to use RemoteEndPoint
1600                  *  - true when addresses must be used (and addresses could be null/empty) */
1601                 bool GetCheckedIPs (SocketAsyncEventArgs e, out IPAddress [] addresses)
1602                 {
1603                         addresses = null;
1604
1605                         // Connect to the first address that match the host name, like:
1606                         // http://blogs.msdn.com/ncl/archive/2009/07/20/new-ncl-features-in-net-4-0-beta-2.aspx
1607                         // while skipping entries that do not match the address family
1608                         DnsEndPoint dep = e.RemoteEndPoint as DnsEndPoint;
1609                         if (dep != null) {
1610                                 addresses = Dns.GetHostAddresses (dep.Host);
1611                                 return true;
1612                         } else {
1613                                 e.ConnectByNameError = null;
1614                                 return false;
1615                         }
1616                 }
1617
1618 #endregion
1619
1620 #region Disconnect
1621
1622                 /* According to the docs, the MS runtime will throw PlatformNotSupportedException
1623                  * if the platform is newer than w2k.  We should be able to cope... */
1624                 public void Disconnect (bool reuseSocket)
1625                 {
1626                         ThrowIfDisposedAndClosed ();
1627
1628                         int error = 0;
1629                         Disconnect_internal (safe_handle, reuseSocket, out error);
1630
1631                         if (error != 0) {
1632                                 if (error == 50) {
1633                                         /* ERROR_NOT_SUPPORTED */
1634                                         throw new PlatformNotSupportedException ();
1635                                 } else {
1636                                         throw new SocketException (error);
1637                                 }
1638                         }
1639
1640                         is_connected = false;
1641                         if (reuseSocket) {
1642                                 /* Do managed housekeeping here... */
1643                         }
1644                 }
1645
1646                 public bool DisconnectAsync (SocketAsyncEventArgs e)
1647                 {
1648                         // NO check is made whether e != null in MS.NET (NRE is thrown in such case)
1649
1650                         ThrowIfDisposedAndClosed ();
1651
1652                         InitSocketAsyncEventArgs (e, DisconnectAsyncCallback, e, SocketOperation.Disconnect);
1653
1654                         IOSelector.Add (e.socket_async_result.Handle, new IOSelectorJob (IOOperation.Write, BeginDisconnectCallback, e.socket_async_result));
1655
1656                         return true;
1657                 }
1658
1659                 static AsyncCallback DisconnectAsyncCallback = new AsyncCallback (ares => {
1660                         SocketAsyncEventArgs e = (SocketAsyncEventArgs) ((SocketAsyncResult) ares).AsyncState;
1661
1662                         if (Interlocked.Exchange (ref e.in_progress, 0) != 1)
1663                                 throw new InvalidOperationException ("No operation in progress");
1664
1665                         try {
1666                                 e.current_socket.EndDisconnect (ares);
1667                         } catch (SocketException ex) {
1668                                 e.SocketError = ex.SocketErrorCode;
1669                         } catch (ObjectDisposedException) {
1670                                 e.SocketError = SocketError.OperationAborted;
1671                         } finally {
1672                                 e.Complete ();
1673                         }
1674                 });
1675
1676                 public IAsyncResult BeginDisconnect (bool reuseSocket, AsyncCallback callback, object state)
1677                 {
1678                         ThrowIfDisposedAndClosed ();
1679
1680                         SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.Disconnect) {
1681                                 ReuseSocket = reuseSocket,
1682                         };
1683
1684                         IOSelector.Add (sockares.Handle, new IOSelectorJob (IOOperation.Write, BeginDisconnectCallback, sockares));
1685
1686                         return sockares;
1687                 }
1688
1689                 static IOAsyncCallback BeginDisconnectCallback = new IOAsyncCallback (ares => {
1690                         SocketAsyncResult sockares = (SocketAsyncResult) ares;
1691
1692                         try {
1693                                 sockares.socket.Disconnect (sockares.ReuseSocket);
1694                         } catch (Exception e) {
1695                                 sockares.Complete (e);
1696                                 return;
1697                         }
1698
1699                         sockares.Complete ();
1700                 });
1701
1702                 public void EndDisconnect (IAsyncResult asyncResult)
1703                 {
1704                         ThrowIfDisposedAndClosed ();
1705
1706                         SocketAsyncResult sockares = ValidateEndIAsyncResult (asyncResult, "EndDisconnect", "asyncResult");
1707
1708                         if (!sockares.IsCompleted)
1709                                 sockares.AsyncWaitHandle.WaitOne ();
1710
1711                         sockares.CheckIfThrowDelayedException ();
1712                 }
1713
1714                 static void Disconnect_internal (SafeSocketHandle safeHandle, bool reuse, out int error)
1715                 {
1716                         bool release = false;
1717                         try {
1718                                 safeHandle.DangerousAddRef (ref release);
1719                                 Disconnect_internal (safeHandle.DangerousGetHandle (), reuse, out error);
1720                         } finally {
1721                                 if (release)
1722                                         safeHandle.DangerousRelease ();
1723                         }
1724                 }
1725
1726                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1727                 extern static void Disconnect_internal (IntPtr sock, bool reuse, out int error);
1728
1729 #endregion
1730
1731 #region Receive
1732
1733                 public int Receive (byte [] buffer)
1734                 {
1735                         return Receive (buffer, SocketFlags.None);
1736                 }
1737
1738                 public int Receive (byte [] buffer, SocketFlags socketFlags)
1739                 {
1740                         ThrowIfDisposedAndClosed ();
1741                         ThrowIfBufferNull (buffer);
1742                         ThrowIfBufferOutOfRange (buffer, 0, buffer.Length);
1743
1744                         SocketError error;
1745                         int ret = Receive_nochecks (buffer, 0, buffer.Length, socketFlags, out error);
1746
1747                         if (error != SocketError.Success) {
1748                                 if (error == SocketError.WouldBlock && is_blocking) // This might happen when ReceiveTimeout is set
1749                                         throw new SocketException ((int) error, TIMEOUT_EXCEPTION_MSG);
1750                                 throw new SocketException ((int) error);
1751                         }
1752
1753                         return ret;
1754                 }
1755
1756                 public int Receive (byte [] buffer, int size, SocketFlags socketFlags)
1757                 {
1758                         ThrowIfDisposedAndClosed ();
1759                         ThrowIfBufferNull (buffer);
1760                         ThrowIfBufferOutOfRange (buffer, 0, size);
1761
1762                         SocketError error;
1763                         int ret = Receive_nochecks (buffer, 0, size, socketFlags, out error);
1764
1765                         if (error != SocketError.Success) {
1766                                 if (error == SocketError.WouldBlock && is_blocking) // This might happen when ReceiveTimeout is set
1767                                         throw new SocketException ((int) error, TIMEOUT_EXCEPTION_MSG);
1768                                 throw new SocketException ((int) error);
1769                         }
1770
1771                         return ret;
1772                 }
1773
1774                 public int Receive (byte [] buffer, int offset, int size, SocketFlags socketFlags)
1775                 {
1776                         ThrowIfDisposedAndClosed ();
1777                         ThrowIfBufferNull (buffer);
1778                         ThrowIfBufferOutOfRange (buffer, offset, size);
1779
1780                         SocketError error;
1781                         int ret = Receive_nochecks (buffer, offset, size, socketFlags, out error);
1782
1783                         if (error != SocketError.Success) {
1784                                 if (error == SocketError.WouldBlock && is_blocking) // This might happen when ReceiveTimeout is set
1785                                         throw new SocketException ((int) error, TIMEOUT_EXCEPTION_MSG);
1786                                 throw new SocketException ((int) error);
1787                         }
1788
1789                         return ret;
1790                 }
1791
1792                 public int Receive (byte [] buffer, int offset, int size, SocketFlags socketFlags, out SocketError errorCode)
1793                 {
1794                         ThrowIfDisposedAndClosed ();
1795                         ThrowIfBufferNull (buffer);
1796                         ThrowIfBufferOutOfRange (buffer, offset, size);
1797
1798                         return Receive_nochecks (buffer, offset, size, socketFlags, out errorCode);
1799                 }
1800
1801                 public int Receive (IList<ArraySegment<byte>> buffers)
1802                 {
1803                         SocketError error;
1804                         int ret = Receive (buffers, SocketFlags.None, out error);
1805
1806                         if (error != SocketError.Success)
1807                                 throw new SocketException ((int) error);
1808
1809                         return ret;
1810                 }
1811
1812                 [CLSCompliant (false)]
1813                 public int Receive (IList<ArraySegment<byte>> buffers, SocketFlags socketFlags)
1814                 {
1815                         SocketError error;
1816                         int ret = Receive (buffers, socketFlags, out error);
1817
1818                         if (error != SocketError.Success)
1819                                 throw new SocketException ((int) error);
1820
1821                         return(ret);
1822                 }
1823
1824                 [CLSCompliant (false)]
1825                 public int Receive (IList<ArraySegment<byte>> buffers, SocketFlags socketFlags, out SocketError errorCode)
1826                 {
1827                         ThrowIfDisposedAndClosed ();
1828
1829                         if (buffers == null || buffers.Count == 0)
1830                                 throw new ArgumentNullException ("buffers");
1831
1832                         int numsegments = buffers.Count;
1833                         int nativeError;
1834                         int ret;
1835
1836                         /* Only example I can find of sending a byte array reference directly into an internal
1837                          * call is in System.Runtime.Remoting/System.Runtime.Remoting.Channels.Ipc.Win32/NamedPipeSocket.cs,
1838                          * so taking a lead from that... */
1839                         WSABUF[] bufarray = new WSABUF[numsegments];
1840                         GCHandle[] gch = new GCHandle[numsegments];
1841
1842                         for (int i = 0; i < numsegments; i++) {
1843                                 ArraySegment<byte> segment = buffers[i];
1844
1845                                 if (segment.Offset < 0 || segment.Count < 0 || segment.Count > segment.Array.Length - segment.Offset)
1846                                         throw new ArgumentOutOfRangeException ("segment");
1847
1848                                 gch[i] = GCHandle.Alloc (segment.Array, GCHandleType.Pinned);
1849                                 bufarray[i].len = segment.Count;
1850                                 bufarray[i].buf = Marshal.UnsafeAddrOfPinnedArrayElement (segment.Array, segment.Offset);
1851                         }
1852
1853                         try {
1854                                 ret = Receive_internal (safe_handle, bufarray, socketFlags, out nativeError);
1855                         } finally {
1856                                 for (int i = 0; i < numsegments; i++) {
1857                                         if (gch[i].IsAllocated)
1858                                                 gch[i].Free ();
1859                                 }
1860                         }
1861
1862                         errorCode = (SocketError) nativeError;
1863
1864                         return ret;
1865                 }
1866
1867                 public bool ReceiveAsync (SocketAsyncEventArgs e)
1868                 {
1869                         // NO check is made whether e != null in MS.NET (NRE is thrown in such case)
1870
1871                         ThrowIfDisposedAndClosed ();
1872
1873                         // LAME SPEC: the ArgumentException is never thrown, instead an NRE is
1874                         // thrown when e.Buffer and e.BufferList are null (works fine when one is
1875                         // set to a valid object)
1876                         if (e.Buffer == null && e.BufferList == null)
1877                                 throw new NullReferenceException ("Either e.Buffer or e.BufferList must be valid buffers.");
1878
1879                         if (e.Buffer == null) {
1880                                 InitSocketAsyncEventArgs (e, ReceiveAsyncCallback, e, SocketOperation.ReceiveGeneric);
1881
1882                                 e.socket_async_result.Buffers = e.BufferList;
1883
1884                                 QueueIOSelectorJob (readQ, e.socket_async_result.Handle, new IOSelectorJob (IOOperation.Read, BeginReceiveGenericCallback, e.socket_async_result));
1885                         } else {
1886                                 InitSocketAsyncEventArgs (e, ReceiveAsyncCallback, e, SocketOperation.Receive);
1887
1888                                 e.socket_async_result.Buffer = e.Buffer;
1889                                 e.socket_async_result.Offset = e.Offset;
1890                                 e.socket_async_result.Size = e.Count;
1891
1892                                 QueueIOSelectorJob (readQ, e.socket_async_result.Handle, new IOSelectorJob (IOOperation.Read, BeginReceiveCallback, e.socket_async_result));
1893                         }
1894
1895                         return true;
1896                 }
1897
1898                 static AsyncCallback ReceiveAsyncCallback = new AsyncCallback (ares => {
1899                         SocketAsyncEventArgs e = (SocketAsyncEventArgs) ((SocketAsyncResult) ares).AsyncState;
1900
1901                         if (Interlocked.Exchange (ref e.in_progress, 0) != 1)
1902                                 throw new InvalidOperationException ("No operation in progress");
1903
1904                         try {
1905                                 e.BytesTransferred = e.current_socket.EndReceive (ares);
1906                         } catch (SocketException se){
1907                                 e.SocketError = se.SocketErrorCode;
1908                         } catch (ObjectDisposedException) {
1909                                 e.SocketError = SocketError.OperationAborted;
1910                         } finally {
1911                                 e.Complete ();
1912                         }
1913                 });
1914
1915                 public IAsyncResult BeginReceive (byte[] buffer, int offset, int size, SocketFlags socket_flags, AsyncCallback callback, object state)
1916                 {
1917                         ThrowIfDisposedAndClosed ();
1918                         ThrowIfBufferNull (buffer);
1919                         ThrowIfBufferOutOfRange (buffer, offset, size);
1920
1921                         SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.Receive) {
1922                                 Buffer = buffer,
1923                                 Offset = offset,
1924                                 Size = size,
1925                                 SockFlags = socket_flags,
1926                         };
1927
1928                         QueueIOSelectorJob (readQ, sockares.Handle, new IOSelectorJob (IOOperation.Read, BeginReceiveCallback, sockares));
1929
1930                         return sockares;
1931                 }
1932
1933                 public IAsyncResult BeginReceive (byte[] buffer, int offset, int size, SocketFlags flags, out SocketError error, AsyncCallback callback, object state)
1934                 {
1935                         /* As far as I can tell from the docs and from experimentation, a pointer to the
1936                          * SocketError parameter is not supposed to be saved for the async parts.  And as we don't
1937                          * set any socket errors in the setup code, we just have to set it to Success. */
1938                         error = SocketError.Success;
1939                         return BeginReceive (buffer, offset, size, flags, callback, state);
1940                 }
1941
1942                 static IOAsyncCallback BeginReceiveCallback = new IOAsyncCallback (ares => {
1943                         SocketAsyncResult sockares = (SocketAsyncResult) ares;
1944                         int total = 0;
1945
1946                         try {
1947                                 total = Receive_internal (sockares.socket.safe_handle, sockares.Buffer, sockares.Offset, sockares.Size, sockares.SockFlags, out sockares.error);
1948                         } catch (Exception e) {
1949                                 sockares.Complete (e);
1950                                 return;
1951                         }
1952
1953                         sockares.Complete (total);
1954                 });
1955
1956                 [CLSCompliant (false)]
1957                 public IAsyncResult BeginReceive (IList<ArraySegment<byte>> buffers, SocketFlags socketFlags, AsyncCallback callback, object state)
1958                 {
1959                         ThrowIfDisposedAndClosed ();
1960
1961                         if (buffers == null)
1962                                 throw new ArgumentNullException ("buffers");
1963
1964                         SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.ReceiveGeneric) {
1965                                 Buffers = buffers,
1966                                 SockFlags = socketFlags,
1967                         };
1968
1969                         QueueIOSelectorJob (readQ, sockares.Handle, new IOSelectorJob (IOOperation.Read, BeginReceiveGenericCallback, sockares));
1970
1971                         return sockares;
1972                 }
1973
1974                 [CLSCompliant (false)]
1975                 public IAsyncResult BeginReceive (IList<ArraySegment<byte>> buffers, SocketFlags socketFlags, out SocketError errorCode, AsyncCallback callback, object state)
1976                 {
1977                         /* I assume the same SocketError semantics as above */
1978                         errorCode = SocketError.Success;
1979                         return BeginReceive (buffers, socketFlags, callback, state);
1980                 }
1981
1982                 static IOAsyncCallback BeginReceiveGenericCallback = new IOAsyncCallback (ares => {
1983                         SocketAsyncResult sockares = (SocketAsyncResult) ares;
1984                         int total = 0;
1985
1986                         try {
1987                                 total = sockares.socket.Receive (sockares.Buffers, sockares.SockFlags);
1988                         } catch (Exception e) {
1989                                 sockares.Complete (e);
1990                                 return;
1991                         }
1992
1993                         sockares.Complete (total);
1994                 });
1995
1996                 public int EndReceive (IAsyncResult result)
1997                 {
1998                         SocketError error;
1999                         int bytesReceived = EndReceive (result, out error);
2000
2001                         if (error != SocketError.Success) {
2002                                 if (error != SocketError.WouldBlock && error != SocketError.InProgress)
2003                                         is_connected = false;
2004                                 throw new SocketException ((int)error);
2005                         }
2006
2007                         return bytesReceived;
2008                 }
2009
2010                 public int EndReceive (IAsyncResult asyncResult, out SocketError errorCode)
2011                 {
2012                         ThrowIfDisposedAndClosed ();
2013
2014                         SocketAsyncResult sockares = ValidateEndIAsyncResult (asyncResult, "EndReceive", "asyncResult");
2015
2016                         if (!sockares.IsCompleted)
2017                                 sockares.AsyncWaitHandle.WaitOne ();
2018
2019                         // If no socket error occurred, call CheckIfThrowDelayedException in case there are other
2020                         // kinds of exceptions that should be thrown.
2021                         if ((errorCode = sockares.ErrorCode) == SocketError.Success)
2022                                 sockares.CheckIfThrowDelayedException();
2023
2024                         return sockares.Total;
2025                 }
2026
2027                 int Receive_nochecks (byte [] buf, int offset, int size, SocketFlags flags, out SocketError error)
2028                 {
2029                         int nativeError;
2030                         int ret = Receive_internal (safe_handle, buf, offset, size, flags, out nativeError);
2031
2032                         error = (SocketError) nativeError;
2033                         if (error != SocketError.Success && error != SocketError.WouldBlock && error != SocketError.InProgress) {
2034                                 is_connected = false;
2035                                 is_bound = false;
2036                         } else {
2037                                 is_connected = true;
2038                         }
2039
2040                         return ret;
2041                 }
2042
2043                 static int Receive_internal (SafeSocketHandle safeHandle, WSABUF[] bufarray, SocketFlags flags, out int error)
2044                 {
2045                         try {
2046                                 safeHandle.RegisterForBlockingSyscall ();
2047                                 return Receive_internal (safeHandle.DangerousGetHandle (), bufarray, flags, out error);
2048                         } finally {
2049                                 safeHandle.UnRegisterForBlockingSyscall ();
2050                         }
2051                 }
2052
2053                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2054                 extern static int Receive_internal (IntPtr sock, WSABUF[] bufarray, SocketFlags flags, out int error);
2055
2056                 static int Receive_internal (SafeSocketHandle safeHandle, byte[] buffer, int offset, int count, SocketFlags flags, out int error)
2057                 {
2058                         try {
2059                                 safeHandle.RegisterForBlockingSyscall ();
2060                                 return Receive_internal (safeHandle.DangerousGetHandle (), buffer, offset, count, flags, out error);
2061                         } finally {
2062                                 safeHandle.UnRegisterForBlockingSyscall ();
2063                         }
2064                 }
2065
2066                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
2067                 extern static int Receive_internal(IntPtr sock, byte[] buffer, int offset, int count, SocketFlags flags, out int error);
2068
2069 #endregion
2070
2071 #region ReceiveFrom
2072
2073                 public int ReceiveFrom (byte [] buffer, ref EndPoint remoteEP)
2074                 {
2075                         ThrowIfDisposedAndClosed ();
2076                         ThrowIfBufferNull (buffer);
2077
2078                         return ReceiveFrom (buffer, 0, buffer.Length, SocketFlags.None, ref remoteEP);
2079                 }
2080
2081                 public int ReceiveFrom (byte [] buffer, SocketFlags socketFlags, ref EndPoint remoteEP)
2082                 {
2083                         ThrowIfDisposedAndClosed ();
2084                         ThrowIfBufferNull (buffer);
2085
2086                         return ReceiveFrom (buffer, 0, buffer.Length, socketFlags, ref remoteEP);
2087                 }
2088
2089                 public int ReceiveFrom (byte [] buffer, int size, SocketFlags socketFlags, ref EndPoint remoteEP)
2090                 {
2091                         ThrowIfDisposedAndClosed ();
2092                         ThrowIfBufferNull (buffer);
2093                         ThrowIfBufferOutOfRange (buffer, 0, size);
2094
2095                         return ReceiveFrom (buffer, 0, size, socketFlags, ref remoteEP);
2096                 }
2097
2098                 public int ReceiveFrom (byte [] buffer, int offset, int size, SocketFlags socketFlags, ref EndPoint remoteEP)
2099                 {
2100                         ThrowIfDisposedAndClosed ();
2101                         ThrowIfBufferNull (buffer);
2102                         ThrowIfBufferOutOfRange (buffer, offset, size);
2103
2104                         if (remoteEP == null)
2105                                 throw new ArgumentNullException ("remoteEP");
2106
2107                         int error;
2108                         return ReceiveFrom_nochecks_exc (buffer, offset, size, socketFlags, ref remoteEP, true, out error);
2109                 }
2110
2111                 public bool ReceiveFromAsync (SocketAsyncEventArgs e)
2112                 {
2113                         ThrowIfDisposedAndClosed ();
2114
2115                         // We do not support recv into multiple buffers yet
2116                         if (e.BufferList != null)
2117                                 throw new NotSupportedException ("Mono doesn't support using BufferList at this point.");
2118                         if (e.RemoteEndPoint == null)
2119                                 throw new ArgumentNullException ("remoteEP", "Value cannot be null.");
2120
2121                         InitSocketAsyncEventArgs (e, ReceiveFromAsyncCallback, e, SocketOperation.ReceiveFrom);
2122
2123                         e.socket_async_result.Buffer = e.Buffer;
2124                         e.socket_async_result.Offset = e.Offset;
2125                         e.socket_async_result.Size = e.Count;
2126                         e.socket_async_result.EndPoint = e.RemoteEndPoint;
2127                         e.socket_async_result.SockFlags = e.SocketFlags;
2128
2129                         QueueIOSelectorJob (readQ, e.socket_async_result.Handle, new IOSelectorJob (IOOperation.Read, BeginReceiveFromCallback, e.socket_async_result));
2130
2131                         return true;
2132                 }
2133
2134                 static AsyncCallback ReceiveFromAsyncCallback = new AsyncCallback (ares => {
2135                         SocketAsyncEventArgs e = (SocketAsyncEventArgs) ((SocketAsyncResult) ares).AsyncState;
2136
2137                         if (Interlocked.Exchange (ref e.in_progress, 0) != 1)
2138                                 throw new InvalidOperationException ("No operation in progress");
2139
2140                         try {
2141                                 e.BytesTransferred = e.current_socket.EndReceiveFrom (ares, ref e.remote_ep);
2142                         } catch (SocketException ex) {
2143                                 e.SocketError = ex.SocketErrorCode;
2144                         } catch (ObjectDisposedException) {
2145                                 e.SocketError = SocketError.OperationAborted;
2146                         } finally {
2147                                 e.Complete ();
2148                         }
2149                 });
2150
2151                 public IAsyncResult BeginReceiveFrom (byte[] buffer, int offset, int size, SocketFlags socket_flags, ref EndPoint remote_end, AsyncCallback callback, object state)
2152                 {
2153                         ThrowIfDisposedAndClosed ();
2154                         ThrowIfBufferNull (buffer);
2155                         ThrowIfBufferOutOfRange (buffer, offset, size);
2156
2157                         if (remote_end == null)
2158                                 throw new ArgumentNullException ("remote_end");
2159
2160                         SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.ReceiveFrom) {
2161                                 Buffer = buffer,
2162                                 Offset = offset,
2163                                 Size = size,
2164                                 SockFlags = socket_flags,
2165                                 EndPoint = remote_end,
2166                         };
2167
2168                         QueueIOSelectorJob (readQ, sockares.Handle, new IOSelectorJob (IOOperation.Read, BeginReceiveFromCallback, sockares));
2169
2170                         return sockares;
2171                 }
2172
2173                 static IOAsyncCallback BeginReceiveFromCallback = new IOAsyncCallback (ares => {
2174                         SocketAsyncResult sockares = (SocketAsyncResult) ares;
2175                         int total = 0;
2176
2177                         try {
2178                                 int error;
2179                                 total = sockares.socket.ReceiveFrom_nochecks_exc (sockares.Buffer, sockares.Offset, sockares.Size, sockares.SockFlags, ref sockares.EndPoint, true, out error);
2180                         } catch (Exception e) {
2181                                 sockares.Complete (e);
2182                                 return;
2183                         }
2184
2185                         sockares.Complete (total);
2186                 });
2187
2188                 public int EndReceiveFrom(IAsyncResult result, ref EndPoint end_point)
2189                 {
2190                         ThrowIfDisposedAndClosed ();
2191
2192                         if (end_point == null)
2193                                 throw new ArgumentNullException ("remote_end");
2194
2195                         SocketAsyncResult sockares = ValidateEndIAsyncResult (result, "EndReceiveFrom", "result");
2196
2197                         if (!sockares.IsCompleted)
2198                                 sockares.AsyncWaitHandle.WaitOne();
2199
2200                         sockares.CheckIfThrowDelayedException();
2201
2202                         end_point = sockares.EndPoint;
2203
2204                         return sockares.Total;
2205                 }
2206
2207                 internal int ReceiveFrom_nochecks_exc (byte [] buf, int offset, int size, SocketFlags flags, ref EndPoint remote_end, bool throwOnError, out int error)
2208                 {
2209                         SocketAddress sockaddr = remote_end.Serialize();
2210
2211                         int cnt = ReceiveFrom_internal (safe_handle, buf, offset, size, flags, ref sockaddr, out error);
2212
2213                         SocketError err = (SocketError) error;
2214                         if (err != 0) {
2215                                 if (err != SocketError.WouldBlock && err != SocketError.InProgress) {
2216                                         is_connected = false;
2217                                 } else if (err == SocketError.WouldBlock && is_blocking) { // This might happen when ReceiveTimeout is set
2218                                         if (throwOnError)       
2219                                                 throw new SocketException ((int) SocketError.TimedOut, TIMEOUT_EXCEPTION_MSG);
2220                                         error = (int) SocketError.TimedOut;
2221                                         return 0;
2222                                 }
2223
2224                                 if (throwOnError)
2225                                         throw new SocketException (error);
2226
2227                                 return 0;
2228                         }
2229
2230                         is_connected = true;
2231                         is_bound = true;
2232
2233                         /* If sockaddr is null then we're a connection oriented protocol and should ignore the
2234                          * remote_end parameter (see MSDN documentation for Socket.ReceiveFrom(...) ) */
2235                         if (sockaddr != null) {
2236                                 /* Stupidly, EndPoint.Create() is an instance method */
2237                                 remote_end = remote_end.Create (sockaddr);
2238                         }
2239
2240                         seed_endpoint = remote_end;
2241
2242                         return cnt;
2243                 }
2244
2245                 static int ReceiveFrom_internal (SafeSocketHandle safeHandle, byte[] buffer, int offset, int count, SocketFlags flags, ref SocketAddress sockaddr, out int error)
2246                 {
2247                         try {
2248                                 safeHandle.RegisterForBlockingSyscall ();
2249                                 return ReceiveFrom_internal (safeHandle.DangerousGetHandle (), buffer, offset, count, flags, ref sockaddr, out error);
2250                         } finally {
2251                                 safeHandle.UnRegisterForBlockingSyscall ();
2252                         }
2253                 }
2254
2255                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
2256                 extern static int ReceiveFrom_internal(IntPtr sock, byte[] buffer, int offset, int count, SocketFlags flags, ref SocketAddress sockaddr, out int error);
2257
2258 #endregion
2259
2260 #region ReceiveMessageFrom
2261
2262                 [MonoTODO ("Not implemented")]
2263                 public int ReceiveMessageFrom (byte[] buffer, int offset, int size, ref SocketFlags socketFlags, ref EndPoint remoteEP, out IPPacketInformation ipPacketInformation)
2264                 {
2265                         ThrowIfDisposedAndClosed ();
2266                         ThrowIfBufferNull (buffer);
2267                         ThrowIfBufferOutOfRange (buffer, offset, size);
2268
2269                         if (remoteEP == null)
2270                                 throw new ArgumentNullException ("remoteEP");
2271
2272                         // FIXME: figure out how we get hold of the IPPacketInformation
2273                         throw new NotImplementedException ();
2274                 }
2275
2276                 [MonoTODO ("Not implemented")]
2277                 public bool ReceiveMessageFromAsync (SocketAsyncEventArgs e)
2278                 {
2279                         // NO check is made whether e != null in MS.NET (NRE is thrown in such case)
2280
2281                         ThrowIfDisposedAndClosed ();
2282
2283                         throw new NotImplementedException ();
2284                 }
2285
2286                 [MonoTODO]
2287                 public IAsyncResult BeginReceiveMessageFrom (byte[] buffer, int offset, int size, SocketFlags socketFlags, ref EndPoint remoteEP, AsyncCallback callback, object state)
2288                 {
2289                         ThrowIfDisposedAndClosed ();
2290                         ThrowIfBufferNull (buffer);
2291                         ThrowIfBufferOutOfRange (buffer, offset, size);
2292
2293                         if (remoteEP == null)
2294                                 throw new ArgumentNullException ("remoteEP");
2295
2296                         throw new NotImplementedException ();
2297                 }
2298
2299                 [MonoTODO]
2300                 public int EndReceiveMessageFrom (IAsyncResult asyncResult, ref SocketFlags socketFlags, ref EndPoint endPoint, out IPPacketInformation ipPacketInformation)
2301                 {
2302                         ThrowIfDisposedAndClosed ();
2303
2304                         if (endPoint == null)
2305                                 throw new ArgumentNullException ("endPoint");
2306
2307                         SocketAsyncResult sockares = ValidateEndIAsyncResult (asyncResult, "EndReceiveMessageFrom", "asyncResult");
2308
2309                         throw new NotImplementedException ();
2310                 }
2311
2312 #endregion
2313
2314 #region Send
2315
2316                 public int Send (byte [] buffer)
2317                 {
2318                         ThrowIfDisposedAndClosed ();
2319                         ThrowIfBufferNull (buffer);
2320                         ThrowIfBufferOutOfRange (buffer, 0, buffer.Length);
2321
2322                         SocketError error;
2323                         int ret = Send_nochecks (buffer, 0, buffer.Length, SocketFlags.None, out error);
2324
2325                         if (error != SocketError.Success)
2326                                 throw new SocketException ((int) error);
2327
2328                         return ret;
2329                 }
2330
2331                 public int Send (byte [] buffer, SocketFlags socketFlags)
2332                 {
2333                         ThrowIfDisposedAndClosed ();
2334                         ThrowIfBufferNull (buffer);
2335                         ThrowIfBufferOutOfRange (buffer, 0, buffer.Length);
2336
2337                         SocketError error;
2338                         int ret = Send_nochecks (buffer, 0, buffer.Length, socketFlags, out error);
2339
2340                         if (error != SocketError.Success)
2341                                 throw new SocketException ((int) error);
2342
2343                         return ret;
2344                 }
2345
2346                 public int Send (byte [] buffer, int size, SocketFlags socketFlags)
2347                 {
2348                         ThrowIfDisposedAndClosed ();
2349                         ThrowIfBufferNull (buffer);
2350                         ThrowIfBufferOutOfRange (buffer, 0, size);
2351
2352                         SocketError error;
2353                         int ret = Send_nochecks (buffer, 0, size, socketFlags, out error);
2354
2355                         if (error != SocketError.Success)
2356                                 throw new SocketException ((int) error);
2357
2358                         return ret;
2359                 }
2360
2361                 public int Send (byte [] buffer, int offset, int size, SocketFlags socketFlags)
2362                 {
2363                         ThrowIfDisposedAndClosed ();
2364                         ThrowIfBufferNull (buffer);
2365                         ThrowIfBufferOutOfRange (buffer, offset, size);
2366
2367                         SocketError error;
2368                         int ret = Send_nochecks (buffer, offset, size, socketFlags, out error);
2369
2370                         if (error != SocketError.Success)
2371                                 throw new SocketException ((int) error);
2372
2373                         return ret;
2374                 }
2375
2376                 public int Send (byte [] buffer, int offset, int size, SocketFlags socketFlags, out SocketError errorCode)
2377                 {
2378                         ThrowIfDisposedAndClosed ();
2379                         ThrowIfBufferNull (buffer);
2380                         ThrowIfBufferOutOfRange (buffer, offset, size);
2381
2382                         return Send_nochecks (buffer, offset, size, socketFlags, out errorCode);
2383                 }
2384
2385                 public
2386                 int Send (IList<ArraySegment<byte>> buffers)
2387                 {
2388                         SocketError error;
2389                         int ret = Send (buffers, SocketFlags.None, out error);
2390
2391                         if (error != SocketError.Success)
2392                                 throw new SocketException ((int) error);
2393
2394                         return ret;
2395                 }
2396
2397                 public
2398                 int Send (IList<ArraySegment<byte>> buffers, SocketFlags socketFlags)
2399                 {
2400                         SocketError error;
2401                         int ret = Send (buffers, socketFlags, out error);
2402
2403                         if (error != SocketError.Success)
2404                                 throw new SocketException ((int) error);
2405
2406                         return ret;
2407                 }
2408
2409                 [CLSCompliant (false)]
2410                 public int Send (IList<ArraySegment<byte>> buffers, SocketFlags socketFlags, out SocketError errorCode)
2411                 {
2412                         ThrowIfDisposedAndClosed ();
2413
2414                         if (buffers == null)
2415                                 throw new ArgumentNullException ("buffers");
2416                         if (buffers.Count == 0)
2417                                 throw new ArgumentException ("Buffer is empty", "buffers");
2418
2419                         int numsegments = buffers.Count;
2420                         int nativeError;
2421                         int ret;
2422
2423                         WSABUF[] bufarray = new WSABUF[numsegments];
2424                         GCHandle[] gch = new GCHandle[numsegments];
2425
2426                         for(int i = 0; i < numsegments; i++) {
2427                                 ArraySegment<byte> segment = buffers[i];
2428
2429                                 if (segment.Offset < 0 || segment.Count < 0 || segment.Count > segment.Array.Length - segment.Offset)
2430                                         throw new ArgumentOutOfRangeException ("segment");
2431
2432                                 gch[i] = GCHandle.Alloc (segment.Array, GCHandleType.Pinned);
2433                                 bufarray[i].len = segment.Count;
2434                                 bufarray[i].buf = Marshal.UnsafeAddrOfPinnedArrayElement (segment.Array, segment.Offset);
2435                         }
2436
2437                         try {
2438                                 ret = Send_internal (safe_handle, bufarray, socketFlags, out nativeError);
2439                         } finally {
2440                                 for(int i = 0; i < numsegments; i++) {
2441                                         if (gch[i].IsAllocated) {
2442                                                 gch[i].Free ();
2443                                         }
2444                                 }
2445                         }
2446
2447                         errorCode = (SocketError)nativeError;
2448
2449                         return ret;
2450                 }
2451
2452                 int Send_nochecks (byte [] buf, int offset, int size, SocketFlags flags, out SocketError error)
2453                 {
2454                         if (size == 0) {
2455                                 error = SocketError.Success;
2456                                 return 0;
2457                         }
2458
2459                         int nativeError;
2460                         int sent = 0;
2461                         do {
2462                                 sent += Send_internal (safe_handle, buf, offset + sent, size - sent, flags, out nativeError);
2463                                 error = (SocketError)nativeError;
2464                                 if (error != SocketError.Success && error != SocketError.WouldBlock && error != SocketError.InProgress) {
2465                                         is_connected = false;
2466                                         is_bound = false;
2467                                         break;
2468                                 } else {
2469                                         is_connected = true;
2470                                 }
2471                         } while (sent < size);
2472
2473                         return sent;
2474                 }
2475
2476                 public bool SendAsync (SocketAsyncEventArgs e)
2477                 {
2478                         // NO check is made whether e != null in MS.NET (NRE is thrown in such case)
2479
2480                         ThrowIfDisposedAndClosed ();
2481
2482                         if (e.Buffer == null && e.BufferList == null)
2483                                 throw new NullReferenceException ("Either e.Buffer or e.BufferList must be valid buffers.");
2484
2485                         if (e.Buffer == null) {
2486                                 InitSocketAsyncEventArgs (e, SendAsyncCallback, e, SocketOperation.SendGeneric);
2487
2488                                 e.socket_async_result.Buffers = e.BufferList;
2489
2490                                 QueueIOSelectorJob (writeQ, e.socket_async_result.Handle, new IOSelectorJob (IOOperation.Write, BeginSendGenericCallback, e.socket_async_result));
2491                         } else {
2492                                 InitSocketAsyncEventArgs (e, SendAsyncCallback, e, SocketOperation.Send);
2493
2494                                 e.socket_async_result.Buffer = e.Buffer;
2495                                 e.socket_async_result.Offset = e.Offset;
2496                                 e.socket_async_result.Size = e.Count;
2497
2498                                 QueueIOSelectorJob (writeQ, e.socket_async_result.Handle, new IOSelectorJob (IOOperation.Write, s => BeginSendCallback ((SocketAsyncResult) s, 0), e.socket_async_result));
2499                         }
2500
2501                         return true;
2502                 }
2503
2504                 static AsyncCallback SendAsyncCallback = new AsyncCallback (ares => {
2505                         SocketAsyncEventArgs e = (SocketAsyncEventArgs) ((SocketAsyncResult) ares).AsyncState;
2506
2507                         if (Interlocked.Exchange (ref e.in_progress, 0) != 1)
2508                                 throw new InvalidOperationException ("No operation in progress");
2509
2510                         try {
2511                                 e.BytesTransferred = e.current_socket.EndSend (ares);
2512                         } catch (SocketException se){
2513                                 e.SocketError = se.SocketErrorCode;
2514                         } catch (ObjectDisposedException) {
2515                                 e.SocketError = SocketError.OperationAborted;
2516                         } finally {
2517                                 e.Complete ();
2518                         }
2519                 });
2520
2521                 public IAsyncResult BeginSend (byte[] buffer, int offset, int size, SocketFlags socketFlags, out SocketError errorCode, AsyncCallback callback, object state)
2522                 {
2523                         if (!is_connected) {
2524                                 errorCode = SocketError.NotConnected;
2525                                 throw new SocketException ((int) errorCode);
2526                         }
2527
2528                         errorCode = SocketError.Success;
2529                         return BeginSend (buffer, offset, size, socketFlags, callback, state);
2530                 }
2531
2532                 public IAsyncResult BeginSend (byte[] buffer, int offset, int size, SocketFlags socket_flags, AsyncCallback callback, object state)
2533                 {
2534                         ThrowIfDisposedAndClosed ();
2535                         ThrowIfBufferNull (buffer);
2536                         ThrowIfBufferOutOfRange (buffer, offset, size);
2537
2538                         if (!is_connected)
2539                                 throw new SocketException ((int)SocketError.NotConnected);
2540
2541                         SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.Send) {
2542                                 Buffer = buffer,
2543                                 Offset = offset,
2544                                 Size = size,
2545                                 SockFlags = socket_flags,
2546                         };
2547
2548                         QueueIOSelectorJob (writeQ, sockares.Handle, new IOSelectorJob (IOOperation.Write, s => BeginSendCallback ((SocketAsyncResult) s, 0), sockares));
2549
2550                         return sockares;
2551                 }
2552
2553                 static void BeginSendCallback (SocketAsyncResult sockares, int sent_so_far)
2554                 {
2555                         int total = 0;
2556
2557                         try {
2558                                 total = Socket.Send_internal (sockares.socket.safe_handle, sockares.Buffer, sockares.Offset, sockares.Size, sockares.SockFlags, out sockares.error);
2559                         } catch (Exception e) {
2560                                 sockares.Complete (e);
2561                                 return;
2562                         }
2563
2564                         if (sockares.error == 0) {
2565                                 sent_so_far += total;
2566                                 sockares.Offset += total;
2567                                 sockares.Size -= total;
2568
2569                                 if (sockares.socket.is_disposed) {
2570                                         sockares.Complete (total);
2571                                         return;
2572                                 }
2573
2574                                 if (sockares.Size > 0) {
2575                                         IOSelector.Add (sockares.Handle, new IOSelectorJob (IOOperation.Write, s => BeginSendCallback ((SocketAsyncResult) s, sent_so_far), sockares));
2576                                         return; // Have to finish writing everything. See bug #74475.
2577                                 }
2578
2579                                 sockares.Total = sent_so_far;
2580                         }
2581
2582                         sockares.Complete (total);
2583                 }
2584
2585                 public IAsyncResult BeginSend (IList<ArraySegment<byte>> buffers, SocketFlags socketFlags, AsyncCallback callback, object state)
2586                 {
2587                         ThrowIfDisposedAndClosed ();
2588
2589                         if (buffers == null)
2590                                 throw new ArgumentNullException ("buffers");
2591                         if (!is_connected)
2592                                 throw new SocketException ((int)SocketError.NotConnected);
2593
2594                         SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.SendGeneric) {
2595                                 Buffers = buffers,
2596                                 SockFlags = socketFlags,
2597                         };
2598
2599                         QueueIOSelectorJob (writeQ, sockares.Handle, new IOSelectorJob (IOOperation.Write, BeginSendGenericCallback, sockares));
2600
2601                         return sockares;
2602                 }
2603
2604                 [CLSCompliant (false)]
2605                 public IAsyncResult BeginSend (IList<ArraySegment<byte>> buffers, SocketFlags socketFlags, out SocketError errorCode, AsyncCallback callback, object state)
2606                 {
2607                         if (!is_connected) {
2608                                 errorCode = SocketError.NotConnected;
2609                                 throw new SocketException ((int)errorCode);
2610                         }
2611
2612                         errorCode = SocketError.Success;
2613                         return BeginSend (buffers, socketFlags, callback, state);
2614                 }
2615
2616                 static IOAsyncCallback BeginSendGenericCallback = new IOAsyncCallback (ares => {
2617                         SocketAsyncResult sockares = (SocketAsyncResult) ares;
2618                         int total = 0;
2619
2620                         try {
2621                                 total = sockares.socket.Send (sockares.Buffers, sockares.SockFlags);
2622                         } catch (Exception e) {
2623                                 sockares.Complete (e);
2624                                 return;
2625                         }
2626
2627                         sockares.Complete (total);
2628                 });
2629
2630                 public int EndSend (IAsyncResult result)
2631                 {
2632                         SocketError error;
2633                         int bytesSent = EndSend (result, out error);
2634
2635                         if (error != SocketError.Success) {
2636                                 if (error != SocketError.WouldBlock && error != SocketError.InProgress)
2637                                         is_connected = false;
2638                                 throw new SocketException ((int)error);
2639                         }
2640
2641                         return bytesSent;
2642                 }
2643
2644                 public int EndSend (IAsyncResult asyncResult, out SocketError errorCode)
2645                 {
2646                         ThrowIfDisposedAndClosed ();
2647
2648                         SocketAsyncResult sockares = ValidateEndIAsyncResult (asyncResult, "EndSend", "asyncResult");
2649
2650                         if (!sockares.IsCompleted)
2651                                 sockares.AsyncWaitHandle.WaitOne ();
2652
2653                         /* If no socket error occurred, call CheckIfThrowDelayedException in
2654                          * case there are other kinds of exceptions that should be thrown.*/
2655                         if ((errorCode = sockares.ErrorCode) == SocketError.Success)
2656                                 sockares.CheckIfThrowDelayedException ();
2657
2658                         return sockares.Total;
2659                 }
2660
2661                 static int Send_internal (SafeSocketHandle safeHandle, WSABUF[] bufarray, SocketFlags flags, out int error)
2662                 {
2663                         bool release = false;
2664                         try {
2665                                 safeHandle.DangerousAddRef (ref release);
2666                                 return Send_internal (safeHandle.DangerousGetHandle (), bufarray, flags, out error);
2667                         } finally {
2668                                 if (release)
2669                                         safeHandle.DangerousRelease ();
2670                         }
2671                 }
2672
2673                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
2674                 extern static int Send_internal (IntPtr sock, WSABUF[] bufarray, SocketFlags flags, out int error);
2675
2676                 static int Send_internal (SafeSocketHandle safeHandle, byte[] buf, int offset, int count, SocketFlags flags, out int error)
2677                 {
2678                         try {
2679                                 safeHandle.RegisterForBlockingSyscall ();
2680                                 return Send_internal (safeHandle.DangerousGetHandle (), buf, offset, count, flags, out error);
2681                         } finally {
2682                                 safeHandle.UnRegisterForBlockingSyscall ();
2683                         }
2684                 }
2685
2686                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
2687                 extern static int Send_internal(IntPtr sock, byte[] buf, int offset, int count, SocketFlags flags, out int error);
2688
2689 #endregion
2690
2691 #region SendTo
2692
2693                 public int SendTo (byte [] buffer, EndPoint remoteEP)
2694                 {
2695                         ThrowIfDisposedAndClosed ();
2696                         ThrowIfBufferNull (buffer);
2697
2698                         return SendTo (buffer, 0, buffer.Length, SocketFlags.None, remoteEP);
2699                 }
2700
2701                 public int SendTo (byte [] buffer, SocketFlags socketFlags, EndPoint remoteEP)
2702                 {
2703                         ThrowIfDisposedAndClosed ();
2704                         ThrowIfBufferNull (buffer);
2705
2706                         return SendTo (buffer, 0, buffer.Length, socketFlags, remoteEP);
2707                 }
2708
2709                 public int SendTo (byte [] buffer, int size, SocketFlags socketFlags, EndPoint remoteEP)
2710                 {
2711                         return SendTo (buffer, 0, size, socketFlags, remoteEP);
2712                 }
2713
2714                 public int SendTo (byte [] buffer, int offset, int size, SocketFlags socketFlags, EndPoint remoteEP)
2715                 {
2716                         ThrowIfDisposedAndClosed ();
2717                         ThrowIfBufferNull (buffer);
2718                         ThrowIfBufferOutOfRange (buffer, offset, size);
2719
2720                         if (remoteEP == null)
2721                                 throw new ArgumentNullException("remoteEP");
2722
2723                         return SendTo_nochecks (buffer, offset, size, socketFlags, remoteEP);
2724                 }
2725
2726                 public bool SendToAsync (SocketAsyncEventArgs e)
2727                 {
2728                         // NO check is made whether e != null in MS.NET (NRE is thrown in such case)
2729
2730                         ThrowIfDisposedAndClosed ();
2731
2732                         if (e.BufferList != null)
2733                                 throw new NotSupportedException ("Mono doesn't support using BufferList at this point.");
2734                         if (e.RemoteEndPoint == null)
2735                                 throw new ArgumentNullException ("remoteEP", "Value cannot be null.");
2736
2737                         InitSocketAsyncEventArgs (e, SendToAsyncCallback, e, SocketOperation.SendTo);
2738
2739                         e.socket_async_result.Buffer = e.Buffer;
2740                         e.socket_async_result.Offset = e.Offset;
2741                         e.socket_async_result.Size = e.Count;
2742                         e.socket_async_result.SockFlags = e.SocketFlags;
2743                         e.socket_async_result.EndPoint = e.RemoteEndPoint;
2744
2745                         QueueIOSelectorJob (writeQ, e.socket_async_result.Handle, new IOSelectorJob (IOOperation.Write, s => BeginSendToCallback ((SocketAsyncResult) s, 0), e.socket_async_result));
2746
2747                         return true;
2748                 }
2749
2750                 static AsyncCallback SendToAsyncCallback = new AsyncCallback (ares => {
2751                         SocketAsyncEventArgs e = (SocketAsyncEventArgs) ((SocketAsyncResult) ares).AsyncState;
2752
2753                         if (Interlocked.Exchange (ref e.in_progress, 0) != 1)
2754                                 throw new InvalidOperationException ("No operation in progress");
2755
2756                         try {
2757                                 e.BytesTransferred = e.current_socket.EndSendTo (ares);
2758                         } catch (SocketException ex) {
2759                                 e.SocketError = ex.SocketErrorCode;
2760                         } catch (ObjectDisposedException) {
2761                                 e.SocketError = SocketError.OperationAborted;
2762                         } finally {
2763                                 e.Complete ();
2764                         }
2765                 });
2766
2767                 public IAsyncResult BeginSendTo(byte[] buffer, int offset, int size, SocketFlags socket_flags, EndPoint remote_end, AsyncCallback callback, object state)
2768                 {
2769                         ThrowIfDisposedAndClosed ();
2770                         ThrowIfBufferNull (buffer);
2771                         ThrowIfBufferOutOfRange (buffer, offset, size);
2772
2773                         SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.SendTo) {
2774                                 Buffer = buffer,
2775                                 Offset = offset,
2776                                 Size = size,
2777                                 SockFlags = socket_flags,
2778                                 EndPoint = remote_end,
2779                         };
2780
2781                         QueueIOSelectorJob (writeQ, sockares.Handle, new IOSelectorJob (IOOperation.Write, s => BeginSendToCallback ((SocketAsyncResult) s, 0), sockares));
2782
2783                         return sockares;
2784                 }
2785
2786                 static void BeginSendToCallback (SocketAsyncResult sockares, int sent_so_far)
2787                 {
2788                         int total = 0;
2789                         try {
2790                                 total = sockares.socket.SendTo_nochecks (sockares.Buffer, sockares.Offset, sockares.Size, sockares.SockFlags, sockares.EndPoint);
2791
2792                                 if (sockares.error == 0) {
2793                                         sent_so_far += total;
2794                                         sockares.Offset += total;
2795                                         sockares.Size -= total;
2796                                 }
2797
2798                                 if (sockares.Size > 0) {
2799                                         IOSelector.Add (sockares.Handle, new IOSelectorJob (IOOperation.Write, s => BeginSendToCallback ((SocketAsyncResult) s, sent_so_far), sockares));
2800                                         return; // Have to finish writing everything. See bug #74475.
2801                                 }
2802
2803                                 sockares.Total = sent_so_far;
2804                         } catch (Exception e) {
2805                                 sockares.Complete (e);
2806                                 return;
2807                         }
2808
2809                         sockares.Complete ();
2810                 }
2811
2812                 public int EndSendTo (IAsyncResult result)
2813                 {
2814                         ThrowIfDisposedAndClosed ();
2815
2816                         SocketAsyncResult sockares = ValidateEndIAsyncResult (result, "EndSendTo", "result");
2817
2818                         if (!sockares.IsCompleted)
2819                                 sockares.AsyncWaitHandle.WaitOne();
2820
2821                         sockares.CheckIfThrowDelayedException();
2822
2823                         return sockares.Total;
2824                 }
2825
2826                 int SendTo_nochecks (byte [] buffer, int offset, int size, SocketFlags flags, EndPoint remote_end)
2827                 {
2828                         int error;
2829                         int ret = SendTo_internal (safe_handle, buffer, offset, size, flags, remote_end.Serialize (), out error);
2830
2831                         SocketError err = (SocketError) error;
2832                         if (err != 0) {
2833                                 if (err != SocketError.WouldBlock && err != SocketError.InProgress)
2834                                         is_connected = false;
2835                                 throw new SocketException (error);
2836                         }
2837
2838                         is_connected = true;
2839                         is_bound = true;
2840                         seed_endpoint = remote_end;
2841
2842                         return ret;
2843                 }
2844
2845                 static int SendTo_internal (SafeSocketHandle safeHandle, byte[] buffer, int offset, int count, SocketFlags flags, SocketAddress sa, out int error)
2846                 {
2847                         try {
2848                                 safeHandle.RegisterForBlockingSyscall ();
2849                                 return SendTo_internal (safeHandle.DangerousGetHandle (), buffer, offset, count, flags, sa, out error);
2850                         } finally {
2851                                 safeHandle.UnRegisterForBlockingSyscall ();
2852                         }
2853                 }
2854
2855                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
2856                 extern static int SendTo_internal (IntPtr sock, byte[] buffer, int offset, int count, SocketFlags flags, SocketAddress sa, out int error);
2857
2858 #endregion
2859
2860 #region SendFile
2861
2862                 public void SendFile (string fileName)
2863                 {
2864                         ThrowIfDisposedAndClosed ();
2865
2866                         if (!is_connected)
2867                                 throw new NotSupportedException ();
2868                         if (!is_blocking)
2869                                 throw new InvalidOperationException ();
2870
2871                         SendFile (fileName, null, null, 0);
2872                 }
2873
2874                 public void SendFile (string fileName, byte[] preBuffer, byte[] postBuffer, TransmitFileOptions flags)
2875                 {
2876                         ThrowIfDisposedAndClosed ();
2877
2878                         if (!is_connected)
2879                                 throw new NotSupportedException ();
2880                         if (!is_blocking)
2881                                 throw new InvalidOperationException ();
2882
2883                         if (!SendFile_internal (safe_handle, fileName, preBuffer, postBuffer, flags)) {
2884                                 SocketException exc = new SocketException ();
2885                                 if (exc.ErrorCode == 2 || exc.ErrorCode == 3)
2886                                         throw new FileNotFoundException ();
2887                                 throw exc;
2888                         }
2889                 }
2890
2891                 public IAsyncResult BeginSendFile (string fileName, AsyncCallback callback, object state)
2892                 {
2893                         ThrowIfDisposedAndClosed ();
2894
2895                         if (!is_connected)
2896                                 throw new NotSupportedException ();
2897                         if (!File.Exists (fileName))
2898                                 throw new FileNotFoundException ();
2899
2900                         return BeginSendFile (fileName, null, null, 0, callback, state);
2901                 }
2902
2903                 public IAsyncResult BeginSendFile (string fileName, byte[] preBuffer, byte[] postBuffer, TransmitFileOptions flags, AsyncCallback callback, object state)
2904                 {
2905                         ThrowIfDisposedAndClosed ();
2906
2907                         if (!is_connected)
2908                                 throw new NotSupportedException ();
2909                         if (!File.Exists (fileName))
2910                                 throw new FileNotFoundException ();
2911
2912                         SendFileHandler handler = new SendFileHandler (SendFile);
2913
2914                         return new SendFileAsyncResult (handler, handler.BeginInvoke (fileName, preBuffer, postBuffer, flags, ar => callback (new SendFileAsyncResult (handler, ar)), state));
2915                 }
2916
2917                 public void EndSendFile (IAsyncResult asyncResult)
2918                 {
2919                         ThrowIfDisposedAndClosed ();
2920
2921                         if (asyncResult == null)
2922                                 throw new ArgumentNullException ("asyncResult");
2923
2924                         SendFileAsyncResult ares = asyncResult as SendFileAsyncResult;
2925                         if (ares == null)
2926                                 throw new ArgumentException ("Invalid IAsyncResult", "asyncResult");
2927
2928                         ares.Delegate.EndInvoke (ares.Original);
2929                 }
2930
2931                 static bool SendFile_internal (SafeSocketHandle safeHandle, string filename, byte [] pre_buffer, byte [] post_buffer, TransmitFileOptions flags)
2932                 {
2933                         try {
2934                                 safeHandle.RegisterForBlockingSyscall ();
2935                                 return SendFile_internal (safeHandle.DangerousGetHandle (), filename, pre_buffer, post_buffer, flags);
2936                         } finally {
2937                                 safeHandle.UnRegisterForBlockingSyscall ();
2938                         }
2939                 }
2940
2941                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
2942                 extern static bool SendFile_internal (IntPtr sock, string filename, byte [] pre_buffer, byte [] post_buffer, TransmitFileOptions flags);
2943
2944                 delegate void SendFileHandler (string fileName, byte [] preBuffer, byte [] postBuffer, TransmitFileOptions flags);
2945
2946                 sealed class SendFileAsyncResult : IAsyncResult {
2947                         IAsyncResult ares;
2948                         SendFileHandler d;
2949
2950                         public SendFileAsyncResult (SendFileHandler d, IAsyncResult ares)
2951                         {
2952                                 this.d = d;
2953                                 this.ares = ares;
2954                         }
2955
2956                         public object AsyncState {
2957                                 get { return ares.AsyncState; }
2958                         }
2959
2960                         public WaitHandle AsyncWaitHandle {
2961                                 get { return ares.AsyncWaitHandle; }
2962                         }
2963
2964                         public bool CompletedSynchronously {
2965                                 get { return ares.CompletedSynchronously; }
2966                         }
2967
2968                         public bool IsCompleted {
2969                                 get { return ares.IsCompleted; }
2970                         }
2971
2972                         public SendFileHandler Delegate {
2973                                 get { return d; }
2974                         }
2975
2976                         public IAsyncResult Original {
2977                                 get { return ares; }
2978                         }
2979                 }
2980
2981 #endregion
2982
2983 #region SendPackets
2984
2985                 [MonoTODO ("Not implemented")]
2986                 public bool SendPacketsAsync (SocketAsyncEventArgs e)
2987                 {
2988                         // NO check is made whether e != null in MS.NET (NRE is thrown in such case)
2989
2990                         ThrowIfDisposedAndClosed ();
2991
2992                         throw new NotImplementedException ();
2993                 }
2994
2995 #endregion
2996
2997 #region DuplicateAndClose
2998
2999 #if !MOBILE
3000                 [MonoLimitation ("We do not support passing sockets across processes, we merely allow this API to pass the socket across AppDomains")]
3001                 public SocketInformation DuplicateAndClose (int targetProcessId)
3002                 {
3003                         var si = new SocketInformation ();
3004                         si.Options =
3005                                 (is_listening      ? SocketInformationOptions.Listening : 0) |
3006                                 (is_connected      ? SocketInformationOptions.Connected : 0) |
3007                                 (is_blocking       ? 0 : SocketInformationOptions.NonBlocking) |
3008                                 (use_overlapped_io ? SocketInformationOptions.UseOnlyOverlappedIO : 0);
3009
3010                         si.ProtocolInformation = Mono.DataConverter.Pack ("iiiil", (int)address_family, (int)socket_type, (int)protocol_type, is_bound ? 1 : 0, (long)Handle);
3011                         safe_handle = null;
3012
3013                         return si;
3014                 }
3015 #endif
3016
3017 #endregion
3018
3019 #region GetSocketOption
3020
3021                 public void GetSocketOption (SocketOptionLevel optionLevel, SocketOptionName optionName, byte [] optionValue)
3022                 {
3023                         ThrowIfDisposedAndClosed ();
3024
3025                         if (optionValue == null)
3026                                 throw new SocketException ((int) SocketError.Fault, "Error trying to dereference an invalid pointer");
3027
3028                         int error;
3029                         GetSocketOption_arr_internal (safe_handle, optionLevel, optionName, ref optionValue, out error);
3030
3031                         if (error != 0)
3032                                 throw new SocketException (error);
3033                 }
3034
3035                 public byte [] GetSocketOption (SocketOptionLevel optionLevel, SocketOptionName optionName, int optionLength)
3036                 {
3037                         ThrowIfDisposedAndClosed ();
3038
3039                         int error;
3040                         byte[] byte_val = new byte [optionLength];
3041                         GetSocketOption_arr_internal (safe_handle, optionLevel, optionName, ref byte_val, out error);
3042
3043                         if (error != 0)
3044                                 throw new SocketException (error);
3045
3046                         return byte_val;
3047                 }
3048
3049                 public object GetSocketOption (SocketOptionLevel optionLevel, SocketOptionName optionName)
3050                 {
3051                         ThrowIfDisposedAndClosed ();
3052
3053                         int error;
3054                         object obj_val;
3055                         GetSocketOption_obj_internal (safe_handle, optionLevel, optionName, out obj_val, out error);
3056
3057                         if (error != 0)
3058                                 throw new SocketException (error);
3059
3060                         if (optionName == SocketOptionName.Linger)
3061                                 return (LingerOption) obj_val;
3062                         else if (optionName == SocketOptionName.AddMembership || optionName == SocketOptionName.DropMembership)
3063                                 return (MulticastOption) obj_val;
3064                         else if (obj_val is int)
3065                                 return (int) obj_val;
3066                         else
3067                                 return obj_val;
3068                 }
3069
3070                 static void GetSocketOption_arr_internal (SafeSocketHandle safeHandle, SocketOptionLevel level, SocketOptionName name, ref byte[] byte_val, out int error)
3071                 {
3072                         bool release = false;
3073                         try {
3074                                 safeHandle.DangerousAddRef (ref release);
3075                                 GetSocketOption_arr_internal (safeHandle.DangerousGetHandle (), level, name, ref byte_val, out error);
3076                         } finally {
3077                                 if (release)
3078                                         safeHandle.DangerousRelease ();
3079                         }
3080                 }
3081
3082                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
3083                 extern static void GetSocketOption_arr_internal(IntPtr socket, SocketOptionLevel level, SocketOptionName name, ref byte[] byte_val, out int error);
3084
3085                 static void GetSocketOption_obj_internal (SafeSocketHandle safeHandle, SocketOptionLevel level, SocketOptionName name, out object obj_val, out int error)
3086                 {
3087                         bool release = false;
3088                         try {
3089                                 safeHandle.DangerousAddRef (ref release);
3090                                 GetSocketOption_obj_internal (safeHandle.DangerousGetHandle (), level, name, out obj_val, out error);
3091                         } finally {
3092                                 if (release)
3093                                         safeHandle.DangerousRelease ();
3094                         }
3095                 }
3096
3097                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
3098                 extern static void GetSocketOption_obj_internal(IntPtr socket, SocketOptionLevel level, SocketOptionName name, out object obj_val, out int error);
3099
3100 #endregion
3101
3102 #region SetSocketOption
3103
3104                 public void SetSocketOption (SocketOptionLevel optionLevel, SocketOptionName optionName, byte [] optionValue)
3105                 {
3106                         ThrowIfDisposedAndClosed ();
3107
3108                         // I'd throw an ArgumentNullException, but this is what MS does.
3109                         if (optionValue == null)
3110                                 throw new SocketException ((int) SocketError.Fault, "Error trying to dereference an invalid pointer");
3111
3112                         int error;
3113                         SetSocketOption_internal (safe_handle, optionLevel, optionName, null, optionValue, 0, out error);
3114
3115                         if (error != 0) {
3116                                 if (error == (int) SocketError.InvalidArgument)
3117                                         throw new ArgumentException ();
3118                                 throw new SocketException (error);
3119                         }
3120                 }
3121
3122                 public void SetSocketOption (SocketOptionLevel optionLevel, SocketOptionName optionName, object optionValue)
3123                 {
3124                         ThrowIfDisposedAndClosed ();
3125
3126                         // NOTE: if a null is passed, the byte[] overload is used instead...
3127                         if (optionValue == null)
3128                                 throw new ArgumentNullException("optionValue");
3129
3130                         int error;
3131
3132                         if (optionLevel == SocketOptionLevel.Socket && optionName == SocketOptionName.Linger) {
3133                                 LingerOption linger = optionValue as LingerOption;
3134                                 if (linger == null)
3135                                         throw new ArgumentException ("A 'LingerOption' value must be specified.", "optionValue");
3136                                 SetSocketOption_internal (safe_handle, optionLevel, optionName, linger, null, 0, out error);
3137                         } else if (optionLevel == SocketOptionLevel.IP && (optionName == SocketOptionName.AddMembership || optionName == SocketOptionName.DropMembership)) {
3138                                 MulticastOption multicast = optionValue as MulticastOption;
3139                                 if (multicast == null)
3140                                         throw new ArgumentException ("A 'MulticastOption' value must be specified.", "optionValue");
3141                                 SetSocketOption_internal (safe_handle, optionLevel, optionName, multicast, null, 0, out error);
3142                         } else if (optionLevel == SocketOptionLevel.IPv6 && (optionName == SocketOptionName.AddMembership || optionName == SocketOptionName.DropMembership)) {
3143                                 IPv6MulticastOption multicast = optionValue as IPv6MulticastOption;
3144                                 if (multicast == null)
3145                                         throw new ArgumentException ("A 'IPv6MulticastOption' value must be specified.", "optionValue");
3146                                 SetSocketOption_internal (safe_handle, optionLevel, optionName, multicast, null, 0, out error);
3147                         } else {
3148                                 throw new ArgumentException ("Invalid value specified.", "optionValue");
3149                         }
3150
3151                         if (error != 0) {
3152                                 if (error == (int) SocketError.InvalidArgument)
3153                                         throw new ArgumentException ();
3154                                 throw new SocketException (error);
3155                         }
3156                 }
3157
3158                 public void SetSocketOption (SocketOptionLevel optionLevel, SocketOptionName optionName, bool optionValue)
3159                 {
3160                         int int_val = optionValue ? 1 : 0;
3161
3162                         SetSocketOption (optionLevel, optionName, int_val);
3163                 }
3164
3165                 public void SetSocketOption (SocketOptionLevel optionLevel, SocketOptionName optionName, int optionValue)
3166                 {
3167                         ThrowIfDisposedAndClosed ();
3168
3169                         if (optionLevel == SocketOptionLevel.Socket && optionName == SocketOptionName.ReuseAddress && optionValue != 0 && !SupportsPortReuse (protocol_type))
3170                                 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.");
3171
3172                         int error;
3173                         SetSocketOption_internal (safe_handle, optionLevel, optionName, null, null, optionValue, out error);
3174
3175                         if (error != 0) {
3176                                 if (error == (int) SocketError.InvalidArgument)
3177                                         throw new ArgumentException ();
3178                                 throw new SocketException (error);
3179                         }
3180                 }
3181
3182                 static void SetSocketOption_internal (SafeSocketHandle safeHandle, SocketOptionLevel level, SocketOptionName name, object obj_val, byte [] byte_val, int int_val, out int error)
3183                 {
3184                         bool release = false;
3185                         try {
3186                                 safeHandle.DangerousAddRef (ref release);
3187                                 SetSocketOption_internal (safeHandle.DangerousGetHandle (), level, name, obj_val, byte_val, int_val, out error);
3188                         } finally {
3189                                 if (release)
3190                                         safeHandle.DangerousRelease ();
3191                         }
3192                 }
3193
3194                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
3195                 extern static void SetSocketOption_internal (IntPtr socket, SocketOptionLevel level, SocketOptionName name, object obj_val, byte [] byte_val, int int_val, out int error);
3196
3197 #endregion
3198
3199 #region IOControl
3200
3201                 public int IOControl (int ioControlCode, byte [] optionInValue, byte [] optionOutValue)
3202                 {
3203                         if (is_disposed)
3204                                 throw new ObjectDisposedException (GetType ().ToString ());
3205
3206                         int error;
3207                         int result = IOControl_internal (safe_handle, ioControlCode, optionInValue, optionOutValue, out error);
3208
3209                         if (error != 0)
3210                                 throw new SocketException (error);
3211                         if (result == -1)
3212                                 throw new InvalidOperationException ("Must use Blocking property instead.");
3213
3214                         return result;
3215                 }
3216
3217                 public int IOControl (IOControlCode ioControlCode, byte[] optionInValue, byte[] optionOutValue)
3218                 {
3219                         return IOControl ((int) ioControlCode, optionInValue, optionOutValue);
3220                 }
3221
3222                 static int IOControl_internal (SafeSocketHandle safeHandle, int ioctl_code, byte [] input, byte [] output, out int error)
3223                 {
3224                         bool release = false;
3225                         try {
3226                                 safeHandle.DangerousAddRef (ref release);
3227                                 return IOControl_internal (safeHandle.DangerousGetHandle (), ioctl_code, input, output, out error);
3228                         } finally {
3229                                 if (release)
3230                                         safeHandle.DangerousRelease ();
3231                         }
3232                 }
3233
3234                 /* See Socket.IOControl, WSAIoctl documentation in MSDN. The common options between UNIX
3235                  * and Winsock are FIONREAD, FIONBIO and SIOCATMARK. Anything else will depend on the system
3236                  * except SIO_KEEPALIVE_VALS which is properly handled on both windows and linux. */
3237                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
3238                 extern static int IOControl_internal (IntPtr sock, int ioctl_code, byte [] input, byte [] output, out int error);
3239
3240 #endregion
3241
3242 #region Close
3243
3244                 public void Close ()
3245                 {
3246                         linger_timeout = 0;
3247                         Dispose ();
3248                 }
3249
3250                 public void Close (int timeout)
3251                 {
3252                         linger_timeout = timeout;
3253                         Dispose ();
3254                 }
3255
3256                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
3257                 internal extern static void Close_internal (IntPtr socket, out int error);
3258
3259 #endregion
3260
3261 #region Shutdown
3262
3263                 public void Shutdown (SocketShutdown how)
3264                 {
3265                         ThrowIfDisposedAndClosed ();
3266
3267                         if (!is_connected)
3268                                 throw new SocketException (10057); // Not connected
3269
3270                         int error;
3271                         Shutdown_internal (safe_handle, how, out error);
3272
3273                         if (error != 0)
3274                                 throw new SocketException (error);
3275                 }
3276
3277                 static void Shutdown_internal (SafeSocketHandle safeHandle, SocketShutdown how, out int error)
3278                 {
3279                         bool release = false;
3280                         try {
3281                                 safeHandle.DangerousAddRef (ref release);
3282                                 Shutdown_internal (safeHandle.DangerousGetHandle (), how, out error);
3283                         } finally {
3284                                 if (release)
3285                                         safeHandle.DangerousRelease ();
3286                         }
3287                 }
3288
3289                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
3290                 internal extern static void Shutdown_internal (IntPtr socket, SocketShutdown how, out int error);
3291
3292 #endregion
3293
3294 #region Dispose
3295
3296                 protected virtual void Dispose (bool disposing)
3297                 {
3298                         if (is_disposed)
3299                                 return;
3300
3301                         is_disposed = true;
3302                         bool was_connected = is_connected;
3303                         is_connected = false;
3304
3305                         if (safe_handle != null) {
3306                                 is_closed = true;
3307                                 IntPtr x = Handle;
3308
3309                                 if (was_connected)
3310                                         Linger (x);
3311
3312                                 safe_handle.Dispose ();
3313                         }
3314                 }
3315
3316                 public void Dispose ()
3317                 {
3318                         Dispose (true);
3319                         GC.SuppressFinalize (this);
3320                 }
3321
3322                 void Linger (IntPtr handle)
3323                 {
3324                         if (!is_connected || linger_timeout <= 0)
3325                                 return;
3326
3327                         /* We don't want to receive any more data */
3328                         int error;
3329                         Shutdown_internal (handle, SocketShutdown.Receive, out error);
3330
3331                         if (error != 0)
3332                                 return;
3333
3334                         int seconds = linger_timeout / 1000;
3335                         int ms = linger_timeout % 1000;
3336                         if (ms > 0) {
3337                                 /* If the other end closes, this will return 'true' with 'Available' == 0 */
3338                                 Poll_internal (handle, SelectMode.SelectRead, ms * 1000, out error);
3339                                 if (error != 0)
3340                                         return;
3341                         }
3342
3343                         if (seconds > 0) {
3344                                 LingerOption linger = new LingerOption (true, seconds);
3345                                 SetSocketOption_internal (handle, SocketOptionLevel.Socket, SocketOptionName.Linger, linger, null, 0, out error);
3346                                 /* Not needed, we're closing upon return */
3347                                 //if (error != 0)
3348                                 //      return;
3349                         }
3350                 }
3351
3352 #endregion
3353
3354                 void ThrowIfDisposedAndClosed (Socket socket)
3355                 {
3356                         if (socket.is_disposed && socket.is_closed)
3357                                 throw new ObjectDisposedException (socket.GetType ().ToString ());
3358                 }
3359
3360                 void ThrowIfDisposedAndClosed ()
3361                 {
3362                         if (is_disposed && is_closed)
3363                                 throw new ObjectDisposedException (GetType ().ToString ());
3364                 }
3365
3366                 void ThrowIfBufferNull (byte[] buffer)
3367                 {
3368                         if (buffer == null)
3369                                 throw new ArgumentNullException ("buffer");
3370                 }
3371
3372                 void ThrowIfBufferOutOfRange (byte[] buffer, int offset, int size)
3373                 {
3374                         if (offset < 0)
3375                                 throw new ArgumentOutOfRangeException ("offset", "offset must be >= 0");
3376                         if (offset > buffer.Length)
3377                                 throw new ArgumentOutOfRangeException ("offset", "offset must be <= buffer.Length");
3378                         if (size < 0)
3379                                 throw new ArgumentOutOfRangeException ("size", "size must be >= 0");
3380                         if (size > buffer.Length - offset)
3381                                 throw new ArgumentOutOfRangeException ("size", "size must be <= buffer.Length - offset");
3382                 }
3383
3384                 void ThrowIfUdp ()
3385                 {
3386                         if (protocol_type == ProtocolType.Udp)
3387                                 throw new SocketException ((int)SocketError.ProtocolOption);
3388                 }
3389
3390                 SocketAsyncResult ValidateEndIAsyncResult (IAsyncResult ares, string methodName, string argName)
3391                 {
3392                         if (ares == null)
3393                                 throw new ArgumentNullException (argName);
3394
3395                         SocketAsyncResult sockares = ares as SocketAsyncResult;
3396                         if (sockares == null)
3397                                 throw new ArgumentException ("Invalid IAsyncResult", argName);
3398                         if (Interlocked.CompareExchange (ref sockares.EndCalled, 1, 0) == 1)
3399                                 throw new InvalidOperationException (methodName + " can only be called once per asynchronous operation");
3400
3401                         return sockares;
3402                 }
3403
3404                 void QueueIOSelectorJob (Queue<KeyValuePair<IntPtr, IOSelectorJob>> queue, IntPtr handle, IOSelectorJob job)
3405                 {
3406                         int count;
3407                         lock (queue) {
3408                                 queue.Enqueue (new KeyValuePair<IntPtr, IOSelectorJob> (handle, job));
3409                                 count = queue.Count;
3410                         }
3411
3412                         if (count == 1)
3413                                 IOSelector.Add (handle, job);
3414                 }
3415
3416                 void InitSocketAsyncEventArgs (SocketAsyncEventArgs e, AsyncCallback callback, object state, SocketOperation operation)
3417                 {
3418                         e.socket_async_result.Init (this, callback, state, operation);
3419                         if (e.AcceptSocket != null) {
3420                                 e.socket_async_result.AcceptSocket = e.AcceptSocket;
3421                         }
3422                         e.current_socket = this;
3423                         e.SetLastOperation (SocketOperationToSocketAsyncOperation (operation));
3424                         e.SocketError = SocketError.Success;
3425                         e.BytesTransferred = 0;
3426                 }
3427
3428                 SocketAsyncOperation SocketOperationToSocketAsyncOperation (SocketOperation op)
3429                 {
3430                         switch (op) {
3431                         case SocketOperation.Connect:
3432                                 return SocketAsyncOperation.Connect;
3433                         case SocketOperation.Accept:
3434                                 return SocketAsyncOperation.Accept;
3435                         case SocketOperation.Disconnect:
3436                                 return SocketAsyncOperation.Disconnect;
3437                         case SocketOperation.Receive:
3438                         case SocketOperation.ReceiveGeneric:
3439                                 return SocketAsyncOperation.Receive;
3440                         case SocketOperation.ReceiveFrom:
3441                                 return SocketAsyncOperation.ReceiveFrom;
3442                         case SocketOperation.Send:
3443                         case SocketOperation.SendGeneric:
3444                                 return SocketAsyncOperation.Send;
3445                         case SocketOperation.SendTo:
3446                                 return SocketAsyncOperation.SendTo;
3447                         default:
3448                                 throw new NotImplementedException (String.Format ("Operation {0} is not implemented", op));
3449                         }
3450                 }
3451                 
3452                 IPEndPoint RemapIPEndPoint (IPEndPoint input) {
3453                         // If socket is DualMode ensure we automatically handle mapping IPv4 addresses to IPv6.
3454                         if (IsDualMode && input.AddressFamily == AddressFamily.InterNetwork)
3455                                 return new IPEndPoint (input.Address.MapToIPv6 (), input.Port);
3456                         
3457                         return input;
3458                 }
3459                 
3460                 [StructLayout (LayoutKind.Sequential)]
3461                 struct WSABUF {
3462                         public int len;
3463                         public IntPtr buf;
3464                 }
3465
3466                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
3467                 internal static extern void cancel_blocking_socket_operation (Thread thread);
3468
3469                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
3470                 internal static extern bool SupportsPortReuse (ProtocolType proto);
3471         }
3472 }
3473