use a 2.0 network stack for MONOTOUCH
[mcs.git] / class / System / System.Net.Sockets / Socket_2_1.cs
blob179708ef009cf7765442c19444db74c298bcbfc9
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 //
10 // Copyright (C) 2001, 2002 Phillip Pearson and Ximian, Inc.
11 // http://www.myelin.co.nz
12 // (c) 2004-2006 Novell, Inc. (http://www.novell.com)
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.
36 using System;
37 using System.Net;
38 using System.Collections;
39 using System.Runtime.CompilerServices;
40 using System.Runtime.InteropServices;
41 using System.Threading;
42 using System.Reflection;
43 using System.IO;
44 using System.Security;
45 using System.Text;
47 #if !NET_2_1
48 using System.Net.Configuration;
49 #endif
51 #if NET_2_0
52 using System.Collections.Generic;
53 #if !NET_2_1
54 using System.Net.NetworkInformation;
55 using System.Timers;
56 #endif
57 #endif
59 namespace System.Net.Sockets {
61 public partial class Socket : IDisposable {
64 * These two fields are looked up by name by the runtime, don't change
65 * their name without also updating the runtime code.
67 private static int ipv4Supported = -1, ipv6Supported = -1;
69 static Socket ()
71 // initialize ipv4Supported and ipv6Supported
72 CheckProtocolSupport ();
75 internal static void CheckProtocolSupport ()
77 if(ipv4Supported == -1) {
78 try {
79 Socket tmp = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
80 tmp.Close();
82 ipv4Supported = 1;
83 } catch {
84 ipv4Supported = 0;
88 if (ipv6Supported == -1) {
89 #if !NET_2_1
90 #if NET_2_0 && CONFIGURATION_DEP
91 SettingsSection config;
92 config = (SettingsSection) System.Configuration.ConfigurationManager.GetSection ("system.net/settings");
93 if (config != null)
94 ipv6Supported = config.Ipv6.Enabled ? -1 : 0;
95 #else
96 NetConfig config = System.Configuration.ConfigurationSettings.GetConfig("system.net/settings") as NetConfig;
97 if (config != null)
98 ipv6Supported = config.ipv6Enabled ? -1 : 0;
99 #endif
100 #endif
101 if (ipv6Supported != 0) {
102 try {
103 Socket tmp = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp);
104 tmp.Close();
106 ipv6Supported = 1;
107 } catch {
108 ipv6Supported = 0;
114 public static bool SupportsIPv4 {
115 get {
116 CheckProtocolSupport();
117 return ipv4Supported == 1;
121 #if NET_2_0
122 [ObsoleteAttribute ("Use OSSupportsIPv6 instead")]
123 #endif
124 public static bool SupportsIPv6 {
125 get {
126 CheckProtocolSupport();
127 return ipv6Supported == 1;
130 #if NET_2_1
131 public static bool OSSupportsIPv4 {
132 get {
133 CheckProtocolSupport();
134 return ipv4Supported == 1;
137 #endif
138 #if NET_2_1
139 public static bool OSSupportsIPv6 {
140 get {
141 CheckProtocolSupport();
142 return ipv6Supported == 1;
145 #elif NET_2_0
146 public static bool OSSupportsIPv6 {
147 get {
148 NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces ();
150 foreach (NetworkInterface adapter in nics) {
151 if (adapter.Supports (NetworkInterfaceComponent.IPv6))
152 return true;
154 return false;
157 #endif
159 /* the field "socket" is looked up by name by the runtime */
160 private IntPtr socket;
161 private AddressFamily address_family;
162 private SocketType socket_type;
163 private ProtocolType protocol_type;
164 internal bool blocking=true;
165 Thread blocking_thread;
166 #if NET_2_0
167 private bool isbound;
168 #endif
169 /* When true, the socket was connected at the time of
170 * the last IO operation
172 private bool connected;
173 /* true if we called Close_internal */
174 private bool closed;
175 internal bool disposed;
178 * This EndPoint is used when creating new endpoints. Because
179 * there are many types of EndPoints possible,
180 * seed_endpoint.Create(addr) is used for creating new ones.
181 * As such, this value is set on Bind, SentTo, ReceiveFrom,
182 * Connect, etc.
184 internal EndPoint seed_endpoint = null;
186 #if !TARGET_JVM
187 // Creates a new system socket, returning the handle
188 [MethodImplAttribute(MethodImplOptions.InternalCall)]
189 private extern IntPtr Socket_internal(AddressFamily family,
190 SocketType type,
191 ProtocolType proto,
192 out int error);
193 #endif
195 public Socket(AddressFamily family, SocketType type, ProtocolType proto)
197 #if NET_2_1
198 if (family == AddressFamily.Unspecified)
199 throw new ArgumentException ("family");
200 #endif
201 address_family=family;
202 socket_type=type;
203 protocol_type=proto;
205 int error;
207 socket = Socket_internal (family, type, proto, out error);
208 if (error != 0)
209 throw new SocketException (error);
210 #if !NET_2_1
211 SocketDefaults ();
212 #endif
215 ~Socket ()
217 Dispose (false);
221 public AddressFamily AddressFamily {
222 get { return address_family; }
225 #if !TARGET_JVM
226 [MethodImplAttribute(MethodImplOptions.InternalCall)]
227 private extern static void Blocking_internal(IntPtr socket,
228 bool block,
229 out int error);
230 #endif
231 public bool Blocking {
232 get {
233 return(blocking);
235 set {
236 if (disposed && closed)
237 throw new ObjectDisposedException (GetType ().ToString ());
239 int error;
241 Blocking_internal (socket, value, out error);
243 if (error != 0)
244 throw new SocketException (error);
246 blocking=value;
250 public bool Connected {
251 get { return connected; }
252 internal set { connected = value; }
255 public ProtocolType ProtocolType {
256 get { return protocol_type; }
258 #if NET_2_0
259 public bool NoDelay {
260 get {
261 if (disposed && closed)
262 throw new ObjectDisposedException (GetType ().ToString ());
264 ThrowIfUpd ();
266 return (int)(GetSocketOption (
267 SocketOptionLevel.Tcp,
268 SocketOptionName.NoDelay)) != 0;
271 set {
272 if (disposed && closed)
273 throw new ObjectDisposedException (GetType ().ToString ());
275 ThrowIfUpd ();
277 SetSocketOption (
278 SocketOptionLevel.Tcp,
279 SocketOptionName.NoDelay, value ? 1 : 0);
283 public int ReceiveBufferSize {
284 get {
285 if (disposed && closed) {
286 throw new ObjectDisposedException (GetType ().ToString ());
288 return((int)GetSocketOption (SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer));
290 set {
291 if (disposed && closed) {
292 throw new ObjectDisposedException (GetType ().ToString ());
294 if (value < 0) {
295 throw new ArgumentOutOfRangeException ("value", "The value specified for a set operation is less than zero");
298 SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer, value);
302 public int SendBufferSize {
303 get {
304 if (disposed && closed) {
305 throw new ObjectDisposedException (GetType ().ToString ());
307 return((int)GetSocketOption (SocketOptionLevel.Socket, SocketOptionName.SendBuffer));
309 set {
310 if (disposed && closed) {
311 throw new ObjectDisposedException (GetType ().ToString ());
313 if (value < 0) {
314 throw new ArgumentOutOfRangeException ("value", "The value specified for a set operation is less than zero");
317 SetSocketOption (SocketOptionLevel.Socket,
318 SocketOptionName.SendBuffer,
319 value);
323 public short Ttl {
324 get {
325 if (disposed && closed) {
326 throw new ObjectDisposedException (GetType ().ToString ());
329 short ttl_val;
331 if (address_family == AddressFamily.InterNetwork) {
332 ttl_val = (short)((int)GetSocketOption (SocketOptionLevel.IP, SocketOptionName.IpTimeToLive));
333 } else if (address_family == AddressFamily.InterNetworkV6) {
334 ttl_val = (short)((int)GetSocketOption (SocketOptionLevel.IPv6, SocketOptionName.HopLimit));
335 } else {
336 throw new NotSupportedException ("This property is only valid for InterNetwork and InterNetworkV6 sockets");
339 return(ttl_val);
341 set {
342 if (disposed && closed) {
343 throw new ObjectDisposedException (GetType ().ToString ());
346 if (address_family == AddressFamily.InterNetwork) {
347 SetSocketOption (SocketOptionLevel.IP, SocketOptionName.IpTimeToLive, value);
348 } else if (address_family == AddressFamily.InterNetworkV6) {
349 SetSocketOption (SocketOptionLevel.IPv6, SocketOptionName.HopLimit, value);
350 } else {
351 throw new NotSupportedException ("This property is only valid for InterNetwork and InterNetworkV6 sockets");
355 #endif
356 // Returns the remote endpoint details in addr and port
357 [MethodImplAttribute(MethodImplOptions.InternalCall)]
358 private extern static SocketAddress RemoteEndPoint_internal(IntPtr socket, out int error);
360 public EndPoint RemoteEndPoint {
361 get {
362 if (disposed && closed)
363 throw new ObjectDisposedException (GetType ().ToString ());
366 * If the seed EndPoint is null, Connect, Bind,
367 * etc has not yet been called. MS returns null
368 * in this case.
370 if (seed_endpoint == null)
371 return null;
373 SocketAddress sa;
374 int error;
376 sa=RemoteEndPoint_internal(socket, out error);
378 if (error != 0)
379 throw new SocketException (error);
381 return seed_endpoint.Create (sa);
385 protected virtual void Dispose (bool explicitDisposing)
387 if (disposed)
388 return;
390 disposed = true;
391 connected = false;
392 if ((int) socket != -1) {
393 int error;
394 closed = true;
395 IntPtr x = socket;
396 socket = (IntPtr) (-1);
397 Close_internal (x, out error);
398 if (blocking_thread != null) {
399 blocking_thread.Abort ();
400 blocking_thread = null;
402 if (error != 0)
403 throw new SocketException (error);
407 #if NET_2_1
408 public void Dispose ()
409 #else
410 void IDisposable.Dispose ()
411 #endif
413 Dispose (true);
414 GC.SuppressFinalize (this);
417 // Closes the socket
418 [MethodImplAttribute(MethodImplOptions.InternalCall)]
419 private extern static void Close_internal(IntPtr socket, out int error);
421 public void Close ()
423 ((IDisposable) this).Dispose ();
426 // Connects to the remote address
427 [MethodImplAttribute(MethodImplOptions.InternalCall)]
428 private extern static void Connect_internal(IntPtr sock,
429 SocketAddress sa,
430 out int error);
432 public void Connect (EndPoint remoteEP)
434 SocketAddress serial = null;
436 if (disposed && closed)
437 throw new ObjectDisposedException (GetType ().ToString ());
439 if (remoteEP == null)
440 throw new ArgumentNullException ("remoteEP");
442 IPEndPoint ep = remoteEP as IPEndPoint;
443 if (ep != null)
444 if (ep.Address.Equals (IPAddress.Any) || ep.Address.Equals (IPAddress.IPv6Any))
445 throw new SocketException ((int) SocketError.AddressNotAvailable);
447 #if NET_2_1 && !MONOTOUCH
448 if (protocol_type != ProtocolType.Tcp)
449 throw new SocketException ((int) SocketError.AccessDenied);
451 DnsEndPoint dep = (remoteEP as DnsEndPoint);
452 if (dep != null)
453 serial = dep.AsIPEndPoint ().Serialize ();
454 else
455 serial = remoteEP.Serialize ();
456 #elif NET_2_0
457 /* TODO: check this for the 1.1 profile too */
458 if (islistening)
459 throw new InvalidOperationException ();
461 serial = remoteEP.Serialize ();
462 #else
463 serial = remoteEP.Serialize ();
464 #endif
466 int error = 0;
468 blocking_thread = Thread.CurrentThread;
469 try {
470 Connect_internal (socket, serial, out error);
471 } catch (ThreadAbortException) {
472 if (disposed) {
473 Thread.ResetAbort ();
474 error = (int) SocketError.Interrupted;
476 } finally {
477 blocking_thread = null;
480 if (error != 0)
481 throw new SocketException (error);
483 connected=true;
485 #if NET_2_0
486 isbound = true;
487 #endif
489 seed_endpoint = remoteEP;
492 #if NET_2_0
493 public bool ReceiveAsync (SocketAsyncEventArgs e)
495 // NO check is made whether e != null in MS.NET (NRE is thrown in such case)
497 // LAME SPEC: the ArgumentException is never thrown, instead an NRE is
498 // thrown when e.Buffer and e.BufferList are null (works fine when one is
499 // set to a valid object)
500 if (disposed && closed)
501 throw new ObjectDisposedException (GetType ().ToString ());
503 // We do not support recv into multiple buffers yet
504 if (e.BufferList != null)
505 throw new NotSupportedException ("Mono doesn't support using BufferList at this point.");
507 e.DoOperation (SocketAsyncOperation.Receive, this);
509 // We always return true for now
510 return true;
513 public bool SendAsync (SocketAsyncEventArgs e)
515 // NO check is made whether e != null in MS.NET (NRE is thrown in such case)
517 if (disposed && closed)
518 throw new ObjectDisposedException (GetType ().ToString ());
519 if (e.Buffer == null && e.BufferList == null)
520 throw new ArgumentException ("Either e.Buffer or e.BufferList must be valid buffers.");
522 e.DoOperation (SocketAsyncOperation.Send, this);
524 // We always return true for now
525 return true;
527 #endif
529 [MethodImplAttribute(MethodImplOptions.InternalCall)]
530 extern static bool Poll_internal (IntPtr socket, SelectMode mode, int timeout, out int error);
532 /* This overload is needed as the async Connect method
533 * also needs to check the socket error status, but
534 * getsockopt(..., SO_ERROR) clears the error.
536 internal bool Poll (int time_us, SelectMode mode, out int socket_error)
538 if (disposed && closed)
539 throw new ObjectDisposedException (GetType ().ToString ());
541 if (mode != SelectMode.SelectRead &&
542 mode != SelectMode.SelectWrite &&
543 mode != SelectMode.SelectError)
544 throw new NotSupportedException ("'mode' parameter is not valid.");
546 int error;
547 bool result = Poll_internal (socket, mode, time_us, out error);
548 if (error != 0)
549 throw new SocketException (error);
551 socket_error = (int)GetSocketOption (SocketOptionLevel.Socket, SocketOptionName.Error);
553 if (mode == SelectMode.SelectWrite && result) {
554 /* Update the connected state; for
555 * non-blocking Connect()s this is
556 * when we can find out that the
557 * connect succeeded.
559 if (socket_error == 0) {
560 connected = true;
564 return result;
567 [MethodImplAttribute(MethodImplOptions.InternalCall)]
568 private extern static int Receive_internal(IntPtr sock,
569 byte[] buffer,
570 int offset,
571 int count,
572 SocketFlags flags,
573 out int error);
575 internal int Receive_nochecks (byte [] buf, int offset, int size, SocketFlags flags, out SocketError error)
577 int nativeError;
578 int ret = Receive_internal (socket, buf, offset, size, flags, out nativeError);
579 error = (SocketError) nativeError;
580 if (error != SocketError.Success && error != SocketError.WouldBlock && error != SocketError.InProgress)
581 connected = false;
582 else
583 connected = true;
585 return ret;
588 [MethodImplAttribute(MethodImplOptions.InternalCall)]
589 private extern static void GetSocketOption_obj_internal(IntPtr socket,
590 SocketOptionLevel level, SocketOptionName name, out object obj_val,
591 out int error);
593 [MethodImplAttribute(MethodImplOptions.InternalCall)]
594 private extern static int Send_internal(IntPtr sock,
595 byte[] buf, int offset,
596 int count,
597 SocketFlags flags,
598 out int error);
600 internal int Send_nochecks (byte [] buf, int offset, int size, SocketFlags flags, out SocketError error)
602 if (size == 0) {
603 error = SocketError.Success;
604 return 0;
607 int nativeError;
609 int ret = Send_internal (socket, buf, offset, size, flags, out nativeError);
611 error = (SocketError)nativeError;
613 if (error != SocketError.Success && error != SocketError.WouldBlock && error != SocketError.InProgress)
614 connected = false;
615 else
616 connected = true;
618 return ret;
621 public object GetSocketOption (SocketOptionLevel optionLevel, SocketOptionName optionName)
623 if (disposed && closed)
624 throw new ObjectDisposedException (GetType ().ToString ());
626 object obj_val;
627 int error;
629 GetSocketOption_obj_internal (socket, optionLevel, optionName, out obj_val,
630 out error);
631 if (error != 0)
632 throw new SocketException (error);
634 if (optionName == SocketOptionName.Linger) {
635 return((LingerOption)obj_val);
636 } else if (optionName == SocketOptionName.AddMembership ||
637 optionName == SocketOptionName.DropMembership) {
638 return((MulticastOption)obj_val);
639 } else if (obj_val is int) {
640 return((int)obj_val);
641 } else {
642 return(obj_val);
646 [MethodImplAttribute (MethodImplOptions.InternalCall)]
647 private extern static void Shutdown_internal (IntPtr socket, SocketShutdown how, out int error);
649 public void Shutdown (SocketShutdown how)
651 if (disposed && closed)
652 throw new ObjectDisposedException (GetType ().ToString ());
654 int error;
656 Shutdown_internal (socket, how, out error);
658 if (error != 0)
659 throw new SocketException (error);
662 [MethodImplAttribute(MethodImplOptions.InternalCall)]
663 private extern static void SetSocketOption_internal (IntPtr socket, SocketOptionLevel level,
664 SocketOptionName name, object obj_val,
665 byte [] byte_val, int int_val,
666 out int error);
668 public void SetSocketOption (SocketOptionLevel optionLevel, SocketOptionName optionName, int optionValue)
670 if (disposed && closed)
671 throw new ObjectDisposedException (GetType ().ToString ());
673 int error;
675 SetSocketOption_internal (socket, optionLevel, optionName, null,
676 null, optionValue, out error);
678 if (error != 0)
679 throw new SocketException (error);
682 private void ThrowIfUpd ()
684 #if !NET_2_1
685 if (protocol_type == ProtocolType.Udp)
686 throw new SocketException ((int)SocketError.ProtocolOption);
687 #endif
690 #if NET_2_1 && !MONOTOUCH
691 static MethodInfo check_socket_policy;
693 static void CheckConnect (SocketAsyncEventArgs e, bool checkPolicy)
695 // NO check is made whether e != null in MS.NET (NRE is thrown in such case)
697 if (e.RemoteEndPoint == null)
698 throw new ArgumentNullException ("remoteEP");
699 if (e.BufferList != null)
700 throw new ArgumentException ("Multiple buffers cannot be used with this method.");
702 if (!checkPolicy)
703 return;
705 e.SocketError = SocketError.AccessDenied;
706 if (check_socket_policy == null) {
707 Type type = Type.GetType ("System.Windows.Browser.Net.CrossDomainPolicyManager, System.Windows.Browser, Version=2.0.5.0, Culture=Neutral, PublicKeyToken=7cec85d7bea7798e");
708 check_socket_policy = type.GetMethod ("CheckEndPoint");
709 if (check_socket_policy == null)
710 throw new SecurityException ();
712 if ((bool) check_socket_policy.Invoke (null, new object [1] { e.RemoteEndPoint }))
713 e.SocketError = SocketError.Success;
716 // only _directly_ used (with false) to download the socket policy
717 internal bool ConnectAsync (SocketAsyncEventArgs e, bool checkPolicy)
719 if (disposed && closed)
720 throw new ObjectDisposedException (GetType ().ToString ());
722 CheckConnect (e, checkPolicy);
724 e.DoOperation (SocketAsyncOperation.Connect, this);
726 // We always return true for now
727 return true;
730 public bool ConnectAsync (SocketAsyncEventArgs e)
732 return ConnectAsync (e, true);
735 public static bool ConnectAsync (SocketType socketType, ProtocolType protocolType, SocketAsyncEventArgs e)
737 CheckConnect (e, true);
739 Socket s = new Socket (AddressFamily.InterNetwork, socketType, protocolType);
740 e.DoOperation (SocketAsyncOperation.Connect, s);
742 // We always return true for now
743 return true;
746 public static void CancelConnectAsync (SocketAsyncEventArgs e)
748 if (e == null)
749 throw new ArgumentNullException ("e");
751 Socket s = e.ConnectSocket;
752 if ((s != null) && (s.blocking_thread != null))
753 s.blocking_thread.Abort ();
755 #endif