// System.Net.Sockets.Socket.cs // // Authors: // Phillip Pearson (pp@myelin.co.nz) // Dick Porter // Gonzalo Paniagua Javier (gonzalo@ximian.com) // Sridhar Kulkarni (sridharkulkarni@gmail.com) // Brian Nickel (brian.nickel@gmail.com) // // Copyright (C) 2001, 2002 Phillip Pearson and Ximian, Inc. // http://www.myelin.co.nz // (c) 2004-2006 Novell, Inc. (http://www.novell.com) // // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: // // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // using System; using System.Net; using System.Collections; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Threading; using System.IO; using System.Security; using System.Text; #if !NET_2_1 using System.Net.Configuration; #endif #if NET_2_0 using System.Collections.Generic; #if !NET_2_1 using System.Net.NetworkInformation; #endif #endif namespace System.Net.Sockets { public partial class Socket : IDisposable { /* * These two fields are looked up by name by the runtime, don't change * their name without also updating the runtime code. */ private static int ipv4Supported = -1, ipv6Supported = -1; int linger_timeout; static Socket () { // initialize ipv4Supported and ipv6Supported CheckProtocolSupport (); } internal static void CheckProtocolSupport () { if(ipv4Supported == -1) { try { Socket tmp = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); tmp.Close(); ipv4Supported = 1; } catch { ipv4Supported = 0; } } if (ipv6Supported == -1) { #if !NET_2_1 #if NET_2_0 && CONFIGURATION_DEP SettingsSection config; config = (SettingsSection) System.Configuration.ConfigurationManager.GetSection ("system.net/settings"); if (config != null) ipv6Supported = config.Ipv6.Enabled ? -1 : 0; #else NetConfig config = System.Configuration.ConfigurationSettings.GetConfig("system.net/settings") as NetConfig; if (config != null) ipv6Supported = config.ipv6Enabled ? -1 : 0; #endif #endif if (ipv6Supported != 0) { try { Socket tmp = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp); tmp.Close(); ipv6Supported = 1; } catch { ipv6Supported = 0; } } } } public static bool SupportsIPv4 { get { CheckProtocolSupport(); return ipv4Supported == 1; } } #if NET_2_0 [ObsoleteAttribute ("Use OSSupportsIPv6 instead")] #endif public static bool SupportsIPv6 { get { CheckProtocolSupport(); return ipv6Supported == 1; } } #if NET_2_1 public static bool OSSupportsIPv4 { get { CheckProtocolSupport(); return ipv4Supported == 1; } } #endif #if NET_2_1 public static bool OSSupportsIPv6 { get { CheckProtocolSupport(); return ipv6Supported == 1; } } #elif NET_2_0 public static bool OSSupportsIPv6 { get { NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces (); foreach (NetworkInterface adapter in nics) { if (adapter.Supports (NetworkInterfaceComponent.IPv6)) return true; } return false; } } #endif /* the field "socket" is looked up by name by the runtime */ private IntPtr socket; private AddressFamily address_family; private SocketType socket_type; private ProtocolType protocol_type; internal bool blocking=true; Thread blocking_thread; #if NET_2_0 private bool isbound; #endif /* When true, the socket was connected at the time of * the last IO operation */ private bool connected; /* true if we called Close_internal */ private bool closed; internal bool disposed; /* * This EndPoint is used when creating new endpoints. Because * there are many types of EndPoints possible, * seed_endpoint.Create(addr) is used for creating new ones. * As such, this value is set on Bind, SentTo, ReceiveFrom, * Connect, etc. */ internal EndPoint seed_endpoint = null; #if !TARGET_JVM // Creates a new system socket, returning the handle [MethodImplAttribute(MethodImplOptions.InternalCall)] private extern IntPtr Socket_internal(AddressFamily family, SocketType type, ProtocolType proto, out int error); #endif public Socket(AddressFamily family, SocketType type, ProtocolType proto) { #if NET_2_1 if (family == AddressFamily.Unspecified) throw new ArgumentException ("family"); #endif address_family=family; socket_type=type; protocol_type=proto; int error; socket = Socket_internal (family, type, proto, out error); if (error != 0) throw new SocketException (error); #if !NET_2_1 SocketDefaults (); #endif } ~Socket () { Dispose (false); } public AddressFamily AddressFamily { get { return address_family; } } #if !TARGET_JVM [MethodImplAttribute(MethodImplOptions.InternalCall)] private extern static void Blocking_internal(IntPtr socket, bool block, out int error); #endif #if !NET_2_1 || MONOTOUCH public bool Blocking { get { return(blocking); } set { if (disposed && closed) throw new ObjectDisposedException (GetType ().ToString ()); int error; Blocking_internal (socket, value, out error); if (error != 0) throw new SocketException (error); blocking=value; } } #endif public bool Connected { get { return connected; } internal set { connected = value; } } public ProtocolType ProtocolType { get { return protocol_type; } } #if NET_2_0 public bool NoDelay { get { if (disposed && closed) throw new ObjectDisposedException (GetType ().ToString ()); ThrowIfUpd (); return (int)(GetSocketOption ( SocketOptionLevel.Tcp, SocketOptionName.NoDelay)) != 0; } set { if (disposed && closed) throw new ObjectDisposedException (GetType ().ToString ()); ThrowIfUpd (); SetSocketOption ( SocketOptionLevel.Tcp, SocketOptionName.NoDelay, value ? 1 : 0); } } public int ReceiveBufferSize { get { if (disposed && closed) { throw new ObjectDisposedException (GetType ().ToString ()); } return((int)GetSocketOption (SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer)); } set { if (disposed && closed) { throw new ObjectDisposedException (GetType ().ToString ()); } if (value < 0) { throw new ArgumentOutOfRangeException ("value", "The value specified for a set operation is less than zero"); } SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer, value); } } public int SendBufferSize { get { if (disposed && closed) { throw new ObjectDisposedException (GetType ().ToString ()); } return((int)GetSocketOption (SocketOptionLevel.Socket, SocketOptionName.SendBuffer)); } set { if (disposed && closed) { throw new ObjectDisposedException (GetType ().ToString ()); } if (value < 0) { throw new ArgumentOutOfRangeException ("value", "The value specified for a set operation is less than zero"); } SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.SendBuffer, value); } } public short Ttl { get { if (disposed && closed) { throw new ObjectDisposedException (GetType ().ToString ()); } short ttl_val; if (address_family == AddressFamily.InterNetwork) { ttl_val = (short)((int)GetSocketOption (SocketOptionLevel.IP, SocketOptionName.IpTimeToLive)); } else if (address_family == AddressFamily.InterNetworkV6) { ttl_val = (short)((int)GetSocketOption (SocketOptionLevel.IPv6, SocketOptionName.HopLimit)); } else { throw new NotSupportedException ("This property is only valid for InterNetwork and InterNetworkV6 sockets"); } return(ttl_val); } set { if (disposed && closed) { throw new ObjectDisposedException (GetType ().ToString ()); } if (address_family == AddressFamily.InterNetwork) { SetSocketOption (SocketOptionLevel.IP, SocketOptionName.IpTimeToLive, value); } else if (address_family == AddressFamily.InterNetworkV6) { SetSocketOption (SocketOptionLevel.IPv6, SocketOptionName.HopLimit, value); } else { throw new NotSupportedException ("This property is only valid for InterNetwork and InterNetworkV6 sockets"); } } } #endif // Returns the remote endpoint details in addr and port [MethodImplAttribute(MethodImplOptions.InternalCall)] private extern static SocketAddress RemoteEndPoint_internal(IntPtr socket, out int error); public EndPoint RemoteEndPoint { get { if (disposed && closed) throw new ObjectDisposedException (GetType ().ToString ()); /* * If the seed EndPoint is null, Connect, Bind, * etc has not yet been called. MS returns null * in this case. */ if (seed_endpoint == null) return null; SocketAddress sa; int error; sa=RemoteEndPoint_internal(socket, out error); if (error != 0) throw new SocketException (error); return seed_endpoint.Create (sa); } } void Linger (IntPtr handle) { if (!connected || linger_timeout <= 0) return; // We don't want to receive any more data int error; Shutdown_internal (handle, SocketShutdown.Receive, out error); if (error != 0) return; int seconds = linger_timeout / 1000; int ms = linger_timeout % 1000; if (ms > 0) { // If the other end closes, this will return 'true' with 'Available' == 0 Poll_internal (handle, SelectMode.SelectRead, ms * 1000, out error); if (error != 0) return; } if (seconds > 0) { LingerOption linger = new LingerOption (true, seconds); SetSocketOption_internal (handle, SocketOptionLevel.Socket, SocketOptionName.Linger, linger, null, 0, out error); /* Not needed, we're closing upon return */ /*if (error != 0) return; */ } } protected virtual void Dispose (bool explicitDisposing) { if (disposed) return; disposed = true; bool was_connected = connected; connected = false; if ((int) socket != -1) { int error; closed = true; IntPtr x = socket; socket = (IntPtr) (-1); Thread th = blocking_thread; if (th != null) { th.Abort (); blocking_thread = null; } if (was_connected) Linger (x); //DateTime start = DateTime.UtcNow; Close_internal (x, out error); //Console.WriteLine ("Time spent in Close_internal: {0}ms", (DateTime.UtcNow - start).TotalMilliseconds); if (error != 0) throw new SocketException (error); } } #if NET_2_1 public void Dispose () #else void IDisposable.Dispose () #endif { Dispose (true); GC.SuppressFinalize (this); } // Closes the socket [MethodImplAttribute(MethodImplOptions.InternalCall)] private extern static void Close_internal(IntPtr socket, out int error); public void Close () { linger_timeout = 0; ((IDisposable) this).Dispose (); } #if NET_2_0 public void Close (int timeout) { linger_timeout = timeout; ((IDisposable) this).Dispose (); } #endif // Connects to the remote address [MethodImplAttribute(MethodImplOptions.InternalCall)] private extern static void Connect_internal(IntPtr sock, SocketAddress sa, out int error); public void Connect (EndPoint remoteEP) { SocketAddress serial = null; if (disposed && closed) throw new ObjectDisposedException (GetType ().ToString ()); if (remoteEP == null) throw new ArgumentNullException ("remoteEP"); IPEndPoint ep = remoteEP as IPEndPoint; if (ep != null) if (ep.Address.Equals (IPAddress.Any) || ep.Address.Equals (IPAddress.IPv6Any)) throw new SocketException ((int) SocketError.AddressNotAvailable); #if NET_2_1 && !MONOTOUCH if (protocol_type != ProtocolType.Tcp) throw new SocketException ((int) SocketError.AccessDenied); #elif NET_2_0 /* TODO: check this for the 1.1 profile too */ if (islistening) throw new InvalidOperationException (); #endif serial = remoteEP.Serialize (); int error = 0; blocking_thread = Thread.CurrentThread; try { Connect_internal (socket, serial, out error); } catch (ThreadAbortException) { if (disposed) { Thread.ResetAbort (); error = (int) SocketError.Interrupted; } } finally { blocking_thread = null; } if (error != 0) throw new SocketException (error); connected=true; #if NET_2_0 isbound = true; #endif seed_endpoint = remoteEP; } #if NET_2_0 public bool ReceiveAsync (SocketAsyncEventArgs e) { // NO check is made whether e != null in MS.NET (NRE is thrown in such case) // // LAME SPEC: the ArgumentException is never thrown, instead an NRE is // thrown when e.Buffer and e.BufferList are null (works fine when one is // set to a valid object) if (disposed && closed) throw new ObjectDisposedException (GetType ().ToString ()); // We do not support recv into multiple buffers yet if (e.BufferList != null) throw new NotSupportedException ("Mono doesn't support using BufferList at this point."); e.DoOperation (SocketAsyncOperation.Receive, this); // We always return true for now return true; } public bool SendAsync (SocketAsyncEventArgs e) { // NO check is made whether e != null in MS.NET (NRE is thrown in such case) if (disposed && closed) throw new ObjectDisposedException (GetType ().ToString ()); if (e.Buffer == null && e.BufferList == null) throw new ArgumentException ("Either e.Buffer or e.BufferList must be valid buffers."); e.DoOperation (SocketAsyncOperation.Send, this); // We always return true for now return true; } #endif [MethodImplAttribute(MethodImplOptions.InternalCall)] extern static bool Poll_internal (IntPtr socket, SelectMode mode, int timeout, out int error); #if !NET_2_1 || MONOTOUCH /* This overload is needed as the async Connect method * also needs to check the socket error status, but * getsockopt(..., SO_ERROR) clears the error. */ internal bool Poll (int time_us, SelectMode mode, out int socket_error) { if (disposed && closed) throw new ObjectDisposedException (GetType ().ToString ()); if (mode != SelectMode.SelectRead && mode != SelectMode.SelectWrite && mode != SelectMode.SelectError) throw new NotSupportedException ("'mode' parameter is not valid."); int error; bool result = Poll_internal (socket, mode, time_us, out error); if (error != 0) throw new SocketException (error); socket_error = (int)GetSocketOption (SocketOptionLevel.Socket, SocketOptionName.Error); if (mode == SelectMode.SelectWrite && result) { /* Update the connected state; for * non-blocking Connect()s this is * when we can find out that the * connect succeeded. */ if (socket_error == 0) { connected = true; } } return result; } #endif [MethodImplAttribute(MethodImplOptions.InternalCall)] private extern static int Receive_internal(IntPtr sock, byte[] buffer, int offset, int count, SocketFlags flags, out int error); internal int Receive_nochecks (byte [] buf, int offset, int size, SocketFlags flags, out SocketError error) { int nativeError; int ret = Receive_internal (socket, buf, offset, size, flags, out nativeError); error = (SocketError) nativeError; if (error != SocketError.Success && error != SocketError.WouldBlock && error != SocketError.InProgress) connected = false; else connected = true; return ret; } [MethodImplAttribute(MethodImplOptions.InternalCall)] private extern static void GetSocketOption_obj_internal(IntPtr socket, SocketOptionLevel level, SocketOptionName name, out object obj_val, out int error); [MethodImplAttribute(MethodImplOptions.InternalCall)] private extern static int Send_internal(IntPtr sock, byte[] buf, int offset, int count, SocketFlags flags, out int error); internal int Send_nochecks (byte [] buf, int offset, int size, SocketFlags flags, out SocketError error) { if (size == 0) { error = SocketError.Success; return 0; } int nativeError; int ret = Send_internal (socket, buf, offset, size, flags, out nativeError); error = (SocketError)nativeError; if (error != SocketError.Success && error != SocketError.WouldBlock && error != SocketError.InProgress) connected = false; else connected = true; return ret; } public object GetSocketOption (SocketOptionLevel optionLevel, SocketOptionName optionName) { if (disposed && closed) throw new ObjectDisposedException (GetType ().ToString ()); object obj_val; int error; GetSocketOption_obj_internal (socket, optionLevel, optionName, out obj_val, out error); if (error != 0) throw new SocketException (error); if (optionName == SocketOptionName.Linger) { return((LingerOption)obj_val); } else if (optionName == SocketOptionName.AddMembership || optionName == SocketOptionName.DropMembership) { return((MulticastOption)obj_val); } else if (obj_val is int) { return((int)obj_val); } else { return(obj_val); } } [MethodImplAttribute (MethodImplOptions.InternalCall)] private extern static void Shutdown_internal (IntPtr socket, SocketShutdown how, out int error); public void Shutdown (SocketShutdown how) { if (disposed && closed) throw new ObjectDisposedException (GetType ().ToString ()); if (!connected) throw new SocketException (10057); // Not connected int error; Shutdown_internal (socket, how, out error); if (error != 0) throw new SocketException (error); } [MethodImplAttribute(MethodImplOptions.InternalCall)] private extern static void SetSocketOption_internal (IntPtr socket, SocketOptionLevel level, SocketOptionName name, object obj_val, byte [] byte_val, int int_val, out int error); public void SetSocketOption (SocketOptionLevel optionLevel, SocketOptionName optionName, int optionValue) { if (disposed && closed) throw new ObjectDisposedException (GetType ().ToString ()); int error; SetSocketOption_internal (socket, optionLevel, optionName, null, null, optionValue, out error); if (error != 0) throw new SocketException (error); } private void ThrowIfUpd () { #if !NET_2_1 if (protocol_type == ProtocolType.Udp) throw new SocketException ((int)SocketError.ProtocolOption); #endif } #if NET_2_1 && !MONOTOUCH static void CheckConnect (SocketAsyncEventArgs e) { // NO check is made whether e != null in MS.NET (NRE is thrown in such case) if (e.RemoteEndPoint == null) throw new ArgumentNullException ("remoteEP"); if (e.BufferList != null) throw new ArgumentException ("Multiple buffers cannot be used with this method."); } public bool ConnectAsync (SocketAsyncEventArgs e) { if (disposed && closed) throw new ObjectDisposedException (GetType ().ToString ()); CheckConnect (e); e.DoOperation (SocketAsyncOperation.Connect, this); // We always return true for now return true; } public static bool ConnectAsync (SocketType socketType, ProtocolType protocolType, SocketAsyncEventArgs e) { // exception ordering requires to check before creating the socket (good thing resource wise too) CheckConnect (e); Socket s = new Socket (AddressFamily.InterNetwork, socketType, protocolType); e.DoOperation (SocketAsyncOperation.Connect, s); // We always return true for now return true; } public static void CancelConnectAsync (SocketAsyncEventArgs e) { if (e == null) throw new ArgumentNullException ("e"); Socket s = e.ConnectSocket; if ((s != null) && (s.blocking_thread != null)) s.blocking_thread.Abort (); } #endif } }