[Socket] Fix Socket.BeginConnect to multiple IPAddress (#4960)
[mono.git] / mcs / class / System / System.Net.Sockets / Socket.cs
index e80e84ac7e840ca81706a7e9e9aa36594c143174..d19756b6c30013be319b87d94a0747081fc47394 100644 (file)
@@ -55,13 +55,6 @@ namespace System.Net.Sockets
                const string TIMEOUT_EXCEPTION_MSG = "A connection attempt failed because the connected party did not properly respond" +
                        "after a period of time, or established connection failed because connected host has failed to respond";
 
-               /*
-                *      These two fields are looked up by name by the runtime, don't change
-                *  their name without also updating the runtime code.
-                */
-               static int ipv4_supported = -1;
-               static int ipv6_supported = -1;
-
                /* true if we called Close_internal */
                bool is_closed;
 
@@ -98,80 +91,8 @@ namespace System.Net.Sockets
                int m_IntCleanedUp;
                internal bool connect_in_progress;
 
-               private static volatile bool s_LoggingEnabled = Logging.On;
-
 #region Constructors
 
-               static Socket ()
-               {
-                       if (ipv4_supported == -1) {
-                               try {
-                                       Socket tmp = new Socket (AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
-                                       tmp.Close();
-
-                                       ipv4_supported = 1;
-                               } catch {
-                                       ipv4_supported = 0;
-                               }
-                       }
-
-                       if (ipv6_supported == -1) {
-                               // We need to put a try/catch around ConfigurationManager methods as will always throw an exception 
-                               // when run in a mono embedded application.  This occurs as embedded applications do not have a setup
-                               // for application config.  The exception is not thrown when called from a normal .NET application. 
-                               //
-                               // We, then, need to guard calls to the ConfigurationManager.  If the config is not found or throws an
-                               // exception, will fall through to the existing Socket / API directly below in the code.
-                               //
-                               // Also note that catching ConfigurationErrorsException specifically would require library dependency
-                               // System.Configuration, and wanted to avoid that.
-#if !MOBILE
-#if CONFIGURATION_DEP
-                               try {
-                                       SettingsSection config;
-                                       config = (SettingsSection) System.Configuration.ConfigurationManager.GetSection ("system.net/settings");
-                                       if (config != null)
-                                               ipv6_supported = config.Ipv6.Enabled ? -1 : 0;
-                               } catch {
-                                       ipv6_supported = -1;
-                               }
-#else
-                               try {
-                                       NetConfig config = System.Configuration.ConfigurationSettings.GetConfig("system.net/settings") as NetConfig;
-                                       if (config != null)
-                                               ipv6_supported = config.ipv6Enabled ? -1 : 0;
-                               } catch {
-                                       ipv6_supported = -1;
-                               }
-#endif
-#endif
-                               if (ipv6_supported != 0) {
-                                       try {
-                                               Socket tmp = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp);
-                                               tmp.Close();
-
-                                               ipv6_supported = 1;
-                                       } catch {
-                                               ipv6_supported = 0;
-                                       }
-                               }
-                       }
-               }
-
-               public Socket(AddressFamily addressFamily, SocketType socketType, ProtocolType protocolType)
-               {
-                       this.addressFamily = addressFamily;
-                       this.socketType = socketType;
-                       this.protocolType = protocolType;
-
-                       int error;
-                       this.m_Handle = new SafeSocketHandle (Socket_internal (addressFamily, socketType, protocolType, out error), true);
-
-                       if (error != 0)
-                               throw new SocketException (error);
-
-                       SocketDefaults ();
-               }
 
                public Socket (SocketInformation socketInformation)
                {
@@ -188,6 +109,8 @@ namespace System.Net.Sockets
                        this.is_bound = (ProtocolType) (int) result [3] != 0;
                        this.m_Handle = new SafeSocketHandle ((IntPtr) (long) result [4], true);
 
+                       InitializeSockets ();
+
                        SocketDefaults ();
                }
 
@@ -200,6 +123,8 @@ namespace System.Net.Sockets
                        
                        this.m_Handle = safe_handle;
                        this.is_connected = true;
+
+                       InitializeSockets ();   
                }
 
                void SocketDefaults ()
@@ -236,54 +161,6 @@ namespace System.Net.Sockets
 
 #region Properties
 
-               [ObsoleteAttribute ("Use OSSupportsIPv4 instead")]
-               public static bool SupportsIPv4 {
-                       get { return ipv4_supported == 1; }
-               }
-
-               [ObsoleteAttribute ("Use OSSupportsIPv6 instead")]
-               public static bool SupportsIPv6 {
-                       get { return ipv6_supported == 1; }
-               }
-
-#if MOBILE
-               public static bool OSSupportsIPv4 {
-                       get { return ipv4_supported == 1; }
-               }
-#else
-               public static bool OSSupportsIPv4 {
-                       get {
-                               NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces ();
-
-                               foreach (NetworkInterface adapter in nics) {
-                                       if (adapter.Supports (NetworkInterfaceComponent.IPv4))
-                                               return true;
-                               }
-
-                               return false;
-                       }
-               }
-#endif
-
-#if MOBILE
-               public static bool OSSupportsIPv6 {
-                       get { return ipv6_supported == 1; }
-               }
-#else
-               public static bool OSSupportsIPv6 {
-                       get {
-                               NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces ();
-
-                               foreach (NetworkInterface adapter in nics) {
-                                       if (adapter.Supports (NetworkInterfaceComponent.IPv6))
-                                               return true;
-                               }
-
-                               return false;
-                       }
-               }
-#endif
-
                public int Available {
                        get {
                                ThrowIfDisposedAndClosed ();
@@ -581,7 +458,7 @@ namespace System.Net.Sockets
                        if (list != null) {
                                foreach (Socket sock in list) {
                                        if (sock == null) // MS throws a NullRef
-                                               throw new ArgumentNullException ("name", "Contains a null element");
+                                               throw new ArgumentNullException (name, "Contains a null element");
                                        sockets.Add (sock);
                                }
                        }
@@ -832,11 +709,11 @@ namespace System.Net.Sockets
                        sockares.Complete (acc_socket, total);
                });
 
-               public Socket EndAccept (IAsyncResult result)
+               public Socket EndAccept (IAsyncResult asyncResult)
                {
                        int bytes;
                        byte[] buffer;
-                       return EndAccept (out buffer, out bytes, result);
+                       return EndAccept (out buffer, out bytes, asyncResult);
                }
 
                public Socket EndAccept (out byte[] buffer, out int bytesTransferred, IAsyncResult asyncResult)
@@ -967,51 +844,6 @@ namespace System.Net.Sockets
                        Connect (Dns.GetHostAddresses (host), port);
                }
 
-               public void Connect (IPAddress[] addresses, int port)
-               {
-                       ThrowIfDisposedAndClosed ();
-
-                       if (addresses == null)
-                               throw new ArgumentNullException ("addresses");
-                       if (this.AddressFamily != AddressFamily.InterNetwork && this.AddressFamily != AddressFamily.InterNetworkV6)
-                               throw new NotSupportedException ("This method is only valid for addresses in the InterNetwork or InterNetworkV6 families");
-                       if (is_listening)
-                               throw new InvalidOperationException ();
-
-                       // FIXME: do non-blocking sockets Poll here?
-                       int error = 0;
-                       foreach (IPAddress address in addresses) {
-                               IPEndPoint iep = new IPEndPoint (address, port);
-                               
-                               iep = RemapIPEndPoint (iep);
-
-                               Connect_internal (m_Handle, iep.Serialize (), out error);
-                               if (error == 0) {
-                                       is_connected = true;
-                                       is_bound = true;
-                                       seed_endpoint = iep;
-                                       return;
-                               }
-                               if (error != (int)SocketError.InProgress && error != (int)SocketError.WouldBlock)
-                                       continue;
-
-                               if (!is_blocking) {
-                                       Poll (-1, SelectMode.SelectWrite);
-                                       error = (int)GetSocketOption (SocketOptionLevel.Socket, SocketOptionName.Error);
-                                       if (error == 0) {
-                                               is_connected = true;
-                                               is_bound = true;
-                                               seed_endpoint = iep;
-                                               return;
-                                       }
-                               }
-                       }
-
-                       if (error != 0)
-                               throw new SocketException (error);
-               }
-
-
                public void Connect (EndPoint remoteEP)
                {
                        ThrowIfDisposedAndClosed ();
@@ -1036,7 +868,7 @@ namespace System.Net.Sockets
                        SocketAddress serial = remoteEP.Serialize ();
 
                        int error = 0;
-                       Connect_internal (m_Handle, serial, out error);
+                       Connect_internal (m_Handle, serial, out error, is_blocking);
 
                        if (error == 0 || error == 10035)
                                seed_endpoint = remoteEP; // Keep the ep around for non-blocking sockets
@@ -1069,12 +901,11 @@ namespace System.Net.Sockets
                                SocketAsyncResult ares;
 
                                if (!GetCheckedIPs (e, out addresses)) {
-                                       e.socket_async_result.EndPoint = e.RemoteEndPoint;
+                                       //NOTE: DualMode may cause Socket's RemoteEndpoint to differ in AddressFamily from the
+                                       // SocketAsyncEventArgs, but the SocketAsyncEventArgs itself is not changed
                                        ares = (SocketAsyncResult) BeginConnect (e.RemoteEndPoint, ConnectAsyncCallback, e);
                                } else {
-                                       DnsEndPoint dep = (e.RemoteEndPoint as DnsEndPoint);
-                                       e.socket_async_result.Addresses = addresses;
-                                       e.socket_async_result.Port = dep.Port;
+                                       DnsEndPoint dep = (DnsEndPoint)e.RemoteEndPoint;
                                        ares = (SocketAsyncResult) BeginConnect (addresses, dep.Port, ConnectAsyncCallback, e);
                                }
 
@@ -1090,12 +921,6 @@ namespace System.Net.Sockets
                        return true;
                }
 
-               public static bool ConnectAsync (SocketType socketType, ProtocolType protocolType, SocketAsyncEventArgs e)
-               {
-                       var sock = new Socket (e.RemoteEndPoint.AddressFamily, socketType, protocolType);
-                       return sock.ConnectAsync (e);
-               }
-
                public static void CancelConnectAsync (SocketAsyncEventArgs e)
                {
                        if (e == null)
@@ -1122,7 +947,7 @@ namespace System.Net.Sockets
                        }
                });
 
-               public IAsyncResult BeginConnect (string host, int port, AsyncCallback callback, object state)
+               public IAsyncResult BeginConnect (string host, int port, AsyncCallback requestCallback, object state)
                {
                        ThrowIfDisposedAndClosed ();
 
@@ -1135,80 +960,27 @@ namespace System.Net.Sockets
                        if (is_listening)
                                throw new InvalidOperationException ();
 
-                       return BeginConnect (Dns.GetHostAddresses (host), port, callback, state);
+                       return BeginConnect (Dns.GetHostAddresses (host), port, requestCallback, state);
                }
 
-               public IAsyncResult BeginConnect (EndPoint end_point, AsyncCallback callback, object state)
+               public IAsyncResult BeginConnect (EndPoint remoteEP, AsyncCallback callback, object state)
                {
                        ThrowIfDisposedAndClosed ();
 
-                       if (end_point == null)
-                               throw new ArgumentNullException ("end_point");
+                       if (remoteEP == null)
+                               throw new ArgumentNullException ("remoteEP");
                        if (is_listening)
                                throw new InvalidOperationException ();
 
                        SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.Connect) {
-                               EndPoint = end_point,
+                               EndPoint = remoteEP,
                        };
 
-                       // Bug #75154: Connect() should not succeed for .Any addresses.
-                       if (end_point is IPEndPoint) {
-                               IPEndPoint ep = (IPEndPoint) end_point;
-                               if (ep.Address.Equals (IPAddress.Any) || ep.Address.Equals (IPAddress.IPv6Any)) {
-                                       sockares.Complete (new SocketException ((int) SocketError.AddressNotAvailable), true);
-                                       return sockares;
-                               }
-                               
-                               end_point = RemapIPEndPoint (ep);
-                       }
-
-                       int error = 0;
-
-                       if (connect_in_progress) {
-                               // This could happen when multiple IPs are used
-                               // Calling connect() again will reset the connection attempt and cause
-                               // an error. Better to just close the socket and move on.
-                               connect_in_progress = false;
-                               m_Handle.Dispose ();
-                               m_Handle = new SafeSocketHandle (Socket_internal (addressFamily, socketType, protocolType, out error), true);
-                               if (error != 0)
-                                       throw new SocketException (error);
-                       }
-
-                       bool blk = is_blocking;
-                       if (blk)
-                               Blocking = false;
-                       Connect_internal (m_Handle, end_point.Serialize (), out error);
-                       if (blk)
-                               Blocking = true;
-
-                       if (error == 0) {
-                               // succeeded synch
-                               is_connected = true;
-                               is_bound = true;
-                               sockares.Complete (true);
-                               return sockares;
-                       }
-
-                       if (error != (int) SocketError.InProgress && error != (int) SocketError.WouldBlock) {
-                               // error synch
-                               is_connected = false;
-                               is_bound = false;
-                               sockares.Complete (new SocketException (error), true);
-                               return sockares;
-                       }
-
-                       // continue asynch
-                       is_connected = false;
-                       is_bound = false;
-                       connect_in_progress = true;
-
-                       IOSelector.Add (sockares.Handle, new IOSelectorJob (IOOperation.Write, BeginConnectCallback, sockares));
-
+                       BeginSConnect (sockares);
                        return sockares;
                }
 
-               public IAsyncResult BeginConnect (IPAddress[] addresses, int port, AsyncCallback callback, object state)
+               public IAsyncResult BeginConnect (IPAddress[] addresses, int port, AsyncCallback requestCallback, object state)
                {
                        ThrowIfDisposedAndClosed ();
 
@@ -1223,46 +995,92 @@ namespace System.Net.Sockets
                        if (is_listening)
                                throw new InvalidOperationException ();
 
-                       SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.Connect) {
+                       SocketAsyncResult sockares = new SocketAsyncResult (this, requestCallback, state, SocketOperation.Connect) {
                                Addresses = addresses,
                                Port = port,
                        };
 
                        is_connected = false;
 
-                       return BeginMConnect (sockares);
+                       BeginMConnect (sockares);
+                       return sockares;
                }
 
-               internal IAsyncResult BeginMConnect (SocketAsyncResult sockares)
+               static void BeginMConnect (SocketAsyncResult sockares)
                {
-                       SocketAsyncResult ares = null;
                        Exception exc = null;
-                       AsyncCallback callback;
 
                        for (int i = sockares.CurrentAddress; i < sockares.Addresses.Length; i++) {
                                try {
                                        sockares.CurrentAddress++;
+                                       sockares.EndPoint = new IPEndPoint (sockares.Addresses [i], sockares.Port);
 
-                                       ares = (SocketAsyncResult) BeginConnect (new IPEndPoint (sockares.Addresses [i], sockares.Port), null, sockares);
-                                       if (ares.IsCompleted && ares.CompletedSynchronously) {
-                                               ares.CheckIfThrowDelayedException ();
-
-                                               callback = ares.AsyncCallback;
-                                               if (callback != null)
-                                                       ThreadPool.UnsafeQueueUserWorkItem (_ => callback (ares), null);
-                                       }
-
-                                       break;
+                                       BeginSConnect (sockares);
+                                       return;
                                } catch (Exception e) {
                                        exc = e;
-                                       ares = null;
                                }
                        }
 
-                       if (ares == null)
-                               throw exc;
+                       throw exc;
+               }
 
-                       return sockares;
+               static void BeginSConnect (SocketAsyncResult sockares)
+               {
+                       EndPoint remoteEP = sockares.EndPoint;
+                       // Bug #75154: Connect() should not succeed for .Any addresses.
+                       if (remoteEP is IPEndPoint) {
+                               IPEndPoint ep = (IPEndPoint) remoteEP;
+                               if (ep.Address.Equals (IPAddress.Any) || ep.Address.Equals (IPAddress.IPv6Any)) {
+                                       sockares.Complete (new SocketException ((int) SocketError.AddressNotAvailable), true);
+                                       return;
+                               }
+
+                               sockares.EndPoint = remoteEP = sockares.socket.RemapIPEndPoint (ep);
+                       }
+
+                       int error = 0;
+
+                       if (sockares.socket.connect_in_progress) {
+                               // This could happen when multiple IPs are used
+                               // Calling connect() again will reset the connection attempt and cause
+                               // an error. Better to just close the socket and move on.
+                               sockares.socket.connect_in_progress = false;
+                               sockares.socket.m_Handle.Dispose ();
+                               sockares.socket.m_Handle = new SafeSocketHandle (sockares.socket.Socket_internal (sockares.socket.addressFamily, sockares.socket.socketType, sockares.socket.protocolType, out error), true);
+                               if (error != 0)
+                                       throw new SocketException (error);
+                       }
+
+                       bool blk = sockares.socket.is_blocking;
+                       if (blk)
+                               sockares.socket.Blocking = false;
+                       Connect_internal (sockares.socket.m_Handle, remoteEP.Serialize (), out error, false);
+                       if (blk)
+                               sockares.socket.Blocking = true;
+
+                       if (error == 0) {
+                               // succeeded synch
+                               sockares.socket.is_connected = true;
+                               sockares.socket.is_bound = true;
+                               sockares.Complete (true);
+                               return;
+                       }
+
+                       if (error != (int) SocketError.InProgress && error != (int) SocketError.WouldBlock) {
+                               // error synch
+                               sockares.socket.is_connected = false;
+                               sockares.socket.is_bound = false;
+                               sockares.Complete (new SocketException (error), true);
+                               return;
+                       }
+
+                       // continue asynch
+                       sockares.socket.is_connected = false;
+                       sockares.socket.is_bound = false;
+                       sockares.socket.connect_in_progress = true;
+
+                       IOSelector.Add (sockares.Handle, new IOSelectorJob (IOOperation.Write, BeginConnectCallback, sockares));
                }
 
                static IOAsyncCallback BeginConnectCallback = new IOAsyncCallback (ares => {
@@ -1273,18 +1091,11 @@ namespace System.Net.Sockets
                                return;
                        }
 
-                       SocketAsyncResult mconnect = sockares.AsyncState as SocketAsyncResult;
-                       bool is_mconnect = mconnect != null && mconnect.Addresses != null;
-
                        try {
-                               EndPoint ep = sockares.EndPoint;
-                               int error_code = (int) sockares.socket.GetSocketOption (SocketOptionLevel.Socket, SocketOptionName.Error);
-
-                               if (error_code == 0) {
-                                       if (is_mconnect)
-                                               sockares = mconnect;
+                               int error = (int) sockares.socket.GetSocketOption (SocketOptionLevel.Socket, SocketOptionName.Error);
 
-                                       sockares.socket.seed_endpoint = ep;
+                               if (error == 0) {
+                                       sockares.socket.seed_endpoint = sockares.EndPoint;
                                        sockares.socket.is_connected = true;
                                        sockares.socket.is_bound = true;
                                        sockares.socket.connect_in_progress = false;
@@ -1293,34 +1104,29 @@ namespace System.Net.Sockets
                                        return;
                                }
 
-                               if (!is_mconnect) {
+                               if (sockares.Addresses == null) {
                                        sockares.socket.connect_in_progress = false;
-                                       sockares.Complete (new SocketException (error_code));
+                                       sockares.Complete (new SocketException (error));
                                        return;
                                }
 
-                               if (mconnect.CurrentAddress >= mconnect.Addresses.Length) {
-                                       mconnect.Complete (new SocketException (error_code));
+                               if (sockares.CurrentAddress >= sockares.Addresses.Length) {
+                                       sockares.Complete (new SocketException (error));
                                        return;
                                }
 
-                               mconnect.socket.BeginMConnect (mconnect);
+                               BeginMConnect (sockares);
                        } catch (Exception e) {
                                sockares.socket.connect_in_progress = false;
-
-                               if (is_mconnect)
-                                       sockares = mconnect;
-
                                sockares.Complete (e);
-                               return;
                        }
                });
 
-               public void EndConnect (IAsyncResult result)
+               public void EndConnect (IAsyncResult asyncResult)
                {
                        ThrowIfDisposedAndClosed ();
 
-                       SocketAsyncResult sockares = ValidateEndIAsyncResult (result, "EndConnect", "result");
+                       SocketAsyncResult sockares = ValidateEndIAsyncResult (asyncResult, "EndConnect", "asyncResult");
 
                        if (!sockares.IsCompleted)
                                sockares.AsyncWaitHandle.WaitOne();
@@ -1328,11 +1134,11 @@ namespace System.Net.Sockets
                        sockares.CheckIfThrowDelayedException();
                }
 
-               static void Connect_internal (SafeSocketHandle safeHandle, SocketAddress sa, out int error)
+               static void Connect_internal (SafeSocketHandle safeHandle, SocketAddress sa, out int error, bool blocking)
                {
                        try {
                                safeHandle.RegisterForBlockingSyscall ();
-                               Connect_internal (safeHandle.DangerousGetHandle (), sa, out error);
+                               Connect_internal (safeHandle.DangerousGetHandle (), sa, out error, blocking);
                        } finally {
                                safeHandle.UnRegisterForBlockingSyscall ();
                        }
@@ -1340,7 +1146,7 @@ namespace System.Net.Sockets
 
                /* Connects to the remote address */
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
-               extern static void Connect_internal(IntPtr sock, SocketAddress sa, out int error);
+               extern static void Connect_internal(IntPtr sock, SocketAddress sa, out int error, bool blocking);
 
                /* Returns :
                 *  - false when it is ok to use RemoteEndPoint
@@ -1484,7 +1290,7 @@ namespace System.Net.Sockets
                        ThrowIfBufferOutOfRange (buffer, offset, size);
 
                        int nativeError;
-                       int ret = Receive_internal (m_Handle, buffer, offset, size, socketFlags, out nativeError);
+                       int ret = Receive_internal (m_Handle, buffer, offset, size, socketFlags, out nativeError, is_blocking);
 
                        errorCode = (SocketError) nativeError;
                        if (errorCode != SocketError.Success && errorCode != SocketError.WouldBlock && errorCode != SocketError.InProgress) {
@@ -1527,7 +1333,7 @@ namespace System.Net.Sockets
                        }
 
                        try {
-                               ret = Receive_internal (m_Handle, bufarray, socketFlags, out nativeError);
+                               ret = Receive_internal (m_Handle, bufarray, socketFlags, out nativeError, is_blocking);
                        } finally {
                                for (int i = 0; i < numsegments; i++) {
                                        if (gch[i].IsAllocated)
@@ -1616,7 +1422,7 @@ namespace System.Net.Sockets
                        int total = 0;
 
                        try {
-                               total = Receive_internal (sockares.socket.m_Handle, sockares.Buffer, sockares.Offset, sockares.Size, sockares.SockFlags, out sockares.error);
+                               total = Receive_internal (sockares.socket.m_Handle, sockares.Buffer, sockares.Offset, sockares.Size, sockares.SockFlags, out sockares.error, sockares.socket.is_blocking);
                        } catch (Exception e) {
                                sockares.Complete (e);
                                return;
@@ -1682,31 +1488,31 @@ namespace System.Net.Sockets
                        return sockares.Total;
                }
 
-               static int Receive_internal (SafeSocketHandle safeHandle, WSABUF[] bufarray, SocketFlags flags, out int error)
+               static int Receive_internal (SafeSocketHandle safeHandle, WSABUF[] bufarray, SocketFlags flags, out int error, bool blocking)
                {
                        try {
                                safeHandle.RegisterForBlockingSyscall ();
-                               return Receive_internal (safeHandle.DangerousGetHandle (), bufarray, flags, out error);
+                               return Receive_internal (safeHandle.DangerousGetHandle (), bufarray, flags, out error, blocking);
                        } finally {
                                safeHandle.UnRegisterForBlockingSyscall ();
                        }
                }
 
                [MethodImplAttribute (MethodImplOptions.InternalCall)]
-               extern static int Receive_internal (IntPtr sock, WSABUF[] bufarray, SocketFlags flags, out int error);
+               extern static int Receive_internal (IntPtr sock, WSABUF[] bufarray, SocketFlags flags, out int error, bool blocking);
 
-               static int Receive_internal (SafeSocketHandle safeHandle, byte[] buffer, int offset, int count, SocketFlags flags, out int error)
+               static int Receive_internal (SafeSocketHandle safeHandle, byte[] buffer, int offset, int count, SocketFlags flags, out int error, bool blocking)
                {
                        try {
                                safeHandle.RegisterForBlockingSyscall ();
-                               return Receive_internal (safeHandle.DangerousGetHandle (), buffer, offset, count, flags, out error);
+                               return Receive_internal (safeHandle.DangerousGetHandle (), buffer, offset, count, flags, out error, blocking);
                        } finally {
                                safeHandle.UnRegisterForBlockingSyscall ();
                        }
                }
 
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
-               extern static int Receive_internal(IntPtr sock, byte[] buffer, int offset, int count, SocketFlags flags, out int error);
+               extern static int Receive_internal(IntPtr sock, byte[] buffer, int offset, int count, SocketFlags flags, out int error, bool blocking);
 
 #endregion
 
@@ -1735,7 +1541,7 @@ namespace System.Net.Sockets
                        SocketAddress sockaddr = remoteEP.Serialize();
 
                        int nativeError;
-                       int cnt = ReceiveFrom_internal (m_Handle, buffer, offset, size, socketFlags, ref sockaddr, out nativeError);
+                       int cnt = ReceiveFrom_internal (m_Handle, buffer, offset, size, socketFlags, ref sockaddr, out nativeError, is_blocking);
 
                        errorCode = (SocketError) nativeError;
                        if (errorCode != SocketError.Success) {
@@ -1803,21 +1609,21 @@ namespace System.Net.Sockets
                        }
                });
 
-               public IAsyncResult BeginReceiveFrom (byte[] buffer, int offset, int size, SocketFlags socket_flags, ref EndPoint remote_end, AsyncCallback callback, object state)
+               public IAsyncResult BeginReceiveFrom (byte[] buffer, int offset, int size, SocketFlags socketFlags, ref EndPoint remoteEP, AsyncCallback callback, object state)
                {
                        ThrowIfDisposedAndClosed ();
                        ThrowIfBufferNull (buffer);
                        ThrowIfBufferOutOfRange (buffer, offset, size);
 
-                       if (remote_end == null)
-                               throw new ArgumentNullException ("remote_end");
+                       if (remoteEP == null)
+                               throw new ArgumentNullException ("remoteEP");
 
                        SocketAsyncResult sockares = new SocketAsyncResult (this, callback, state, SocketOperation.ReceiveFrom) {
                                Buffer = buffer,
                                Offset = offset,
                                Size = size,
-                               SockFlags = socket_flags,
-                               EndPoint = remote_end,
+                               SockFlags = socketFlags,
+                               EndPoint = remoteEP,
                        };
 
                        QueueIOSelectorJob (ReadSem, sockares.Handle, new IOSelectorJob (IOOperation.Read, BeginReceiveFromCallback, sockares));
@@ -1845,39 +1651,39 @@ namespace System.Net.Sockets
                        sockares.Complete (total);
                });
 
-               public int EndReceiveFrom(IAsyncResult result, ref EndPoint end_point)
+               public int EndReceiveFrom(IAsyncResult asyncResult, ref EndPoint endPoint)
                {
                        ThrowIfDisposedAndClosed ();
 
-                       if (end_point == null)
-                               throw new ArgumentNullException ("remote_end");
+                       if (endPoint == null)
+                               throw new ArgumentNullException ("endPoint");
 
-                       SocketAsyncResult sockares = ValidateEndIAsyncResult (result, "EndReceiveFrom", "result");
+                       SocketAsyncResult sockares = ValidateEndIAsyncResult (asyncResult, "EndReceiveFrom", "asyncResult");
 
                        if (!sockares.IsCompleted)
                                sockares.AsyncWaitHandle.WaitOne();
 
                        sockares.CheckIfThrowDelayedException();
 
-                       end_point = sockares.EndPoint;
+                       endPoint = sockares.EndPoint;
 
                        return sockares.Total;
                }
 
 
 
-               static int ReceiveFrom_internal (SafeSocketHandle safeHandle, byte[] buffer, int offset, int count, SocketFlags flags, ref SocketAddress sockaddr, out int error)
+               static int ReceiveFrom_internal (SafeSocketHandle safeHandle, byte[] buffer, int offset, int count, SocketFlags flags, ref SocketAddress sockaddr, out int error, bool blocking)
                {
                        try {
                                safeHandle.RegisterForBlockingSyscall ();
-                               return ReceiveFrom_internal (safeHandle.DangerousGetHandle (), buffer, offset, count, flags, ref sockaddr, out error);
+                               return ReceiveFrom_internal (safeHandle.DangerousGetHandle (), buffer, offset, count, flags, ref sockaddr, out error, blocking);
                        } finally {
                                safeHandle.UnRegisterForBlockingSyscall ();
                        }
                }
 
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
-               extern static int ReceiveFrom_internal(IntPtr sock, byte[] buffer, int offset, int count, SocketFlags flags, ref SocketAddress sockaddr, out int error);
+               extern static int ReceiveFrom_internal(IntPtr sock, byte[] buffer, int offset, int count, SocketFlags flags, ref SocketAddress sockaddr, out int error, bool blocking);
 
 #endregion
 
@@ -1928,7 +1734,7 @@ namespace System.Net.Sockets
                        if (endPoint == null)
                                throw new ArgumentNullException ("endPoint");
 
-                       SocketAsyncResult sockares = ValidateEndIAsyncResult (asyncResult, "EndReceiveMessageFrom", "asyncResult");
+                       /*SocketAsyncResult sockares =*/ ValidateEndIAsyncResult (asyncResult, "EndReceiveMessageFrom", "asyncResult");
 
                        throw new NotImplementedException ();
                }
@@ -1952,7 +1758,7 @@ namespace System.Net.Sockets
                        int sent = 0;
                        do {
                                sent += Send_internal (
-m_Handle, buffer, offset + sent, size - sent, socketFlags, out nativeError);
+m_Handle, buffer, offset + sent, size - sent, socketFlags, out nativeError, is_blocking);
                                errorCode = (SocketError)nativeError;
                                if (errorCode != SocketError.Success && errorCode != SocketError.WouldBlock && errorCode != SocketError.InProgress) {
                                        is_connected = false;
@@ -1995,7 +1801,7 @@ m_Handle, buffer, offset + sent, size - sent, socketFlags, out nativeError);
                        }
 
                        try {
-                               ret = Send_internal (m_Handle, bufarray, socketFlags, out nativeError);
+                               ret = Send_internal (m_Handle, bufarray, socketFlags, out nativeError, is_blocking);
                        } finally {
                                for(int i = 0; i < numsegments; i++) {
                                        if (gch[i].IsAllocated) {
@@ -2084,7 +1890,7 @@ m_Handle, buffer, offset + sent, size - sent, socketFlags, out nativeError);
                        int total = 0;
 
                        try {
-                               total = Socket.Send_internal (sockares.socket.m_Handle, sockares.Buffer, sockares.Offset, sockares.Size, sockares.SockFlags, out sockares.error);
+                               total = Socket.Send_internal (sockares.socket.m_Handle, sockares.Buffer, sockares.Offset, sockares.Size, sockares.SockFlags, out sockares.error, false);
                        } catch (Exception e) {
                                sockares.Complete (e);
                                return;
@@ -2172,33 +1978,31 @@ m_Handle, buffer, offset + sent, size - sent, socketFlags, out nativeError);
                        return sockares.Total;
                }
 
-               static int Send_internal (SafeSocketHandle safeHandle, WSABUF[] bufarray, SocketFlags flags, out int error)
+               static int Send_internal (SafeSocketHandle safeHandle, WSABUF[] bufarray, SocketFlags flags, out int error, bool blocking)
                {
-                       bool release = false;
                        try {
-                               safeHandle.DangerousAddRef (ref release);
-                               return Send_internal (safeHandle.DangerousGetHandle (), bufarray, flags, out error);
+                               safeHandle.RegisterForBlockingSyscall ();
+                               return Send_internal (safeHandle.DangerousGetHandle (), bufarray, flags, out error, blocking);
                        } finally {
-                               if (release)
-                                       safeHandle.DangerousRelease ();
+                               safeHandle.UnRegisterForBlockingSyscall ();
                        }
                }
 
                [MethodImplAttribute (MethodImplOptions.InternalCall)]
-               extern static int Send_internal (IntPtr sock, WSABUF[] bufarray, SocketFlags flags, out int error);
+               extern static int Send_internal (IntPtr sock, WSABUF[] bufarray, SocketFlags flags, out int error, bool blocking);
 
-               static int Send_internal (SafeSocketHandle safeHandle, byte[] buf, int offset, int count, SocketFlags flags, out int error)
+               static int Send_internal (SafeSocketHandle safeHandle, byte[] buf, int offset, int count, SocketFlags flags, out int error, bool blocking)
                {
                        try {
                                safeHandle.RegisterForBlockingSyscall ();
-                               return Send_internal (safeHandle.DangerousGetHandle (), buf, offset, count, flags, out error);
+                               return Send_internal (safeHandle.DangerousGetHandle (), buf, offset, count, flags, out error, blocking);
                        } finally {
                                safeHandle.UnRegisterForBlockingSyscall ();
                        }
                }
 
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
-               extern static int Send_internal(IntPtr sock, byte[] buf, int offset, int count, SocketFlags flags, out int error);
+               extern static int Send_internal(IntPtr sock, byte[] buf, int offset, int count, SocketFlags flags, out int error, bool blocking);
 
 #endregion
 
@@ -2214,7 +2018,7 @@ m_Handle, buffer, offset + sent, size - sent, socketFlags, out nativeError);
                                throw new ArgumentNullException("remoteEP");
 
                        int error;
-                       int ret = SendTo_internal (m_Handle, buffer, offset, size, socketFlags, remoteEP.Serialize (), out error);
+                       int ret = SendTo_internal (m_Handle, buffer, offset, size, socketFlags, remoteEP.Serialize (), out error, is_blocking);
 
                        SocketError err = (SocketError) error;
                        if (err != 0) {
@@ -2271,7 +2075,7 @@ m_Handle, buffer, offset + sent, size - sent, socketFlags, out nativeError);
                        }
                });
 
-               public IAsyncResult BeginSendTo(byte[] buffer, int offset, int size, SocketFlags socket_flags, EndPoint remote_end, AsyncCallback callback, object state)
+               public IAsyncResult BeginSendTo(byte[] buffer, int offset, int size, SocketFlags socketFlags, EndPoint remoteEP, AsyncCallback callback, object state)
                {
                        ThrowIfDisposedAndClosed ();
                        ThrowIfBufferNull (buffer);
@@ -2281,8 +2085,8 @@ m_Handle, buffer, offset + sent, size - sent, socketFlags, out nativeError);
                                Buffer = buffer,
                                Offset = offset,
                                Size = size,
-                               SockFlags = socket_flags,
-                               EndPoint = remote_end,
+                               SockFlags = socketFlags,
+                               EndPoint = remoteEP,
                        };
 
                        QueueIOSelectorJob (WriteSem, sockares.Handle, new IOSelectorJob (IOOperation.Write, s => BeginSendToCallback ((SocketAsyncResult) s, 0), sockares));
@@ -2316,11 +2120,11 @@ m_Handle, buffer, offset + sent, size - sent, socketFlags, out nativeError);
                        sockares.Complete ();
                }
 
-               public int EndSendTo (IAsyncResult result)
+               public int EndSendTo (IAsyncResult asyncResult)
                {
                        ThrowIfDisposedAndClosed ();
 
-                       SocketAsyncResult sockares = ValidateEndIAsyncResult (result, "EndSendTo", "result");
+                       SocketAsyncResult sockares = ValidateEndIAsyncResult (asyncResult, "EndSendTo", "result");
 
                        if (!sockares.IsCompleted)
                                sockares.AsyncWaitHandle.WaitOne();
@@ -2330,18 +2134,18 @@ m_Handle, buffer, offset + sent, size - sent, socketFlags, out nativeError);
                        return sockares.Total;
                }
 
-               static int SendTo_internal (SafeSocketHandle safeHandle, byte[] buffer, int offset, int count, SocketFlags flags, SocketAddress sa, out int error)
+               static int SendTo_internal (SafeSocketHandle safeHandle, byte[] buffer, int offset, int count, SocketFlags flags, SocketAddress sa, out int error, bool blocking)
                {
                        try {
                                safeHandle.RegisterForBlockingSyscall ();
-                               return SendTo_internal (safeHandle.DangerousGetHandle (), buffer, offset, count, flags, sa, out error);
+                               return SendTo_internal (safeHandle.DangerousGetHandle (), buffer, offset, count, flags, sa, out error, blocking);
                        } finally {
                                safeHandle.UnRegisterForBlockingSyscall ();
                        }
                }
 
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
-               extern static int SendTo_internal (IntPtr sock, byte[] buffer, int offset, int count, SocketFlags flags, SocketAddress sa, out int error);
+               extern static int SendTo_internal (IntPtr sock, byte[] buffer, int offset, int count, SocketFlags flags, SocketAddress sa, out int error, bool blocking);
 
 #endregion
 
@@ -2356,8 +2160,9 @@ m_Handle, buffer, offset + sent, size - sent, socketFlags, out nativeError);
                        if (!is_blocking)
                                throw new InvalidOperationException ();
 
-                       if (!SendFile_internal (m_Handle, fileName, preBuffer, postBuffer, flags)) {
-                               SocketException exc = new SocketException ();
+                       int error = 0;
+                       if (!SendFile_internal (m_Handle, fileName, preBuffer, postBuffer, flags, out error, is_blocking) || error != 0) {
+                               SocketException exc = new SocketException (error);
                                if (exc.ErrorCode == 2 || exc.ErrorCode == 3)
                                        throw new FileNotFoundException ();
                                throw exc;
@@ -2392,18 +2197,18 @@ m_Handle, buffer, offset + sent, size - sent, socketFlags, out nativeError);
                        ares.Delegate.EndInvoke (ares.Original);
                }
 
-               static bool SendFile_internal (SafeSocketHandle safeHandle, string filename, byte [] pre_buffer, byte [] post_buffer, TransmitFileOptions flags)
+               static bool SendFile_internal (SafeSocketHandle safeHandle, string filename, byte [] pre_buffer, byte [] post_buffer, TransmitFileOptions flags, out int error, bool blocking)
                {
                        try {
                                safeHandle.RegisterForBlockingSyscall ();
-                               return SendFile_internal (safeHandle.DangerousGetHandle (), filename, pre_buffer, post_buffer, flags);
+                               return SendFile_internal (safeHandle.DangerousGetHandle (), filename, pre_buffer, post_buffer, flags, out error, blocking);
                        } finally {
                                safeHandle.UnRegisterForBlockingSyscall ();
                        }
                }
 
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
-               extern static bool SendFile_internal (IntPtr sock, string filename, byte [] pre_buffer, byte [] post_buffer, TransmitFileOptions flags);
+               extern static bool SendFile_internal (IntPtr sock, string filename, byte [] pre_buffer, byte [] post_buffer, TransmitFileOptions flags, out int error, bool blocking);
 
                delegate void SendFileHandler (string fileName, byte [] preBuffer, byte [] postBuffer, TransmitFileOptions flags);
 
@@ -2470,10 +2275,15 @@ m_Handle, buffer, offset + sent, size - sent, socketFlags, out nativeError);
                                (is_blocking       ? 0 : SocketInformationOptions.NonBlocking) |
                                (useOverlappedIO ? SocketInformationOptions.UseOnlyOverlappedIO : 0);
 
-                       si.ProtocolInformation = Mono.DataConverter.Pack ("iiiil", (int)addressFamily, (int)socketType, (int)protocolType, is_bound ? 1 : 0, (long)Handle);
-                       m_Handle = null;
+                       MonoIOError error;
+                       IntPtr duplicateHandle;
+                       if (!MonoIO.DuplicateHandle (System.Diagnostics.Process.GetCurrentProcess ().Handle, Handle, new IntPtr (targetProcessId), out duplicateHandle, 0, 0, 0x00000002 /* DUPLICATE_SAME_ACCESS */, out error))
+                               throw MonoIO.GetException (error);
 
-                       return si;
+                       si.ProtocolInformation = Mono.DataConverter.Pack ("iiiil", (int)addressFamily, (int)socketType, (int)protocolType, is_bound ? 1 : 0, (long)duplicateHandle);
+                       m_Handle = null;
+                       return si;
                }
 
 #endregion
@@ -2628,9 +2438,6 @@ m_Handle, buffer, offset + sent, size - sent, socketFlags, out nativeError);
                {
                        ThrowIfDisposedAndClosed ();
 
-                       if (optionLevel == SocketOptionLevel.Socket && optionName == SocketOptionName.ReuseAddress && optionValue != 0 && !SupportsPortReuse (protocolType))
-                               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.");
-
                        int error;
                        SetSocketOption_internal (m_Handle, optionLevel, optionName, null, null, optionValue, out error);
 
@@ -2854,14 +2661,25 @@ m_Handle, buffer, offset + sent, size - sent, socketFlags, out nativeError);
 
                void QueueIOSelectorJob (SemaphoreSlim sem, IntPtr handle, IOSelectorJob job)
                {
-                       sem.WaitAsync ().ContinueWith (t => {
+                       var task = sem.WaitAsync();
+                       // fast path without Task<Action> allocation.
+                       if (task.IsCompleted) {
                                if (CleanedUp) {
                                        job.MarkDisposed ();
                                        return;
                                }
-
                                IOSelector.Add (handle, job);
-                       });
+                       }
+                       else
+                       {
+                               task.ContinueWith( t => {
+                                       if (CleanedUp) {
+                                               job.MarkDisposed ();
+                                               return;
+                                       }
+                                       IOSelector.Add(handle, job);
+                               });
+                       }
                }
 
                void InitSocketAsyncEventArgs (SocketAsyncEventArgs e, AsyncCallback callback, object state, SocketOperation operation)
@@ -2919,6 +2737,41 @@ m_Handle, buffer, offset + sent, size - sent, socketFlags, out nativeError);
 
                [MethodImplAttribute(MethodImplOptions.InternalCall)]
                internal static extern bool SupportsPortReuse (ProtocolType proto);
+
+               internal static int FamilyHint {
+                       get {
+                               // Returns one of
+                               //      MONO_HINT_UNSPECIFIED           = 0,
+                               //      MONO_HINT_IPV4                          = 1,
+                               //      MONO_HINT_IPV6                          = 2,
+
+                               int hint = 0;
+                               if (OSSupportsIPv4) {
+                                       hint = 1;
+                               }
+
+                               if (OSSupportsIPv6) {
+                                       hint = hint == 0 ? 2 : 0;
+                               }
+
+                               return hint;
+                       }
+               }
+
+               static bool IsProtocolSupported (NetworkInterfaceComponent networkInterface)
+               {
+#if MOBILE
+                       return true;
+#else
+                       var nics = NetworkInterface.GetAllNetworkInterfaces ();
+                       foreach (var adapter in nics) {
+                               if (adapter.Supports (networkInterface))
+                                       return true;
+                       }
+
+                       return false;
+#endif
+               }
        }
 }