* Monodoc/man-provider.cs: NEVER return a non-null string from
[mono.git] / mcs / class / System / System.Net.Sockets / Socket_2_1.cs
1 // System.Net.Sockets.Socket.cs
2 //
3 // Authors:
4 //      Phillip Pearson (pp@myelin.co.nz)
5 //      Dick Porter <dick@ximian.com>
6 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 //      Sridhar Kulkarni (sridharkulkarni@gmail.com)
8 //      Brian Nickel (brian.nickel@gmail.com)
9 //
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)
13 //
14
15 //
16 // Permission is hereby granted, free of charge, to any person obtaining
17 // a copy of this software and associated documentation files (the
18 // "Software"), to deal in the Software without restriction, including
19 // without limitation the rights to use, copy, modify, merge, publish,
20 // distribute, sublicense, and/or sell copies of the Software, and to
21 // permit persons to whom the Software is furnished to do so, subject to
22 // the following conditions:
23 // 
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
26 // 
27 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
31 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
32 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 //
35
36 using System;
37 using System.Net;
38 using System.Collections;
39 using System.Runtime.CompilerServices;
40 using System.Runtime.InteropServices;
41 using System.Threading;
42 using System.Reflection;
43 using System.IO;
44 using System.Net.Configuration;
45 using System.Text;
46
47 #if NET_2_0
48 using System.Collections.Generic;
49 using System.Net.NetworkInformation;
50 #if !NET_2_1
51 using System.Timers;
52 #endif
53 #endif
54
55 namespace System.Net.Sockets {
56
57         public partial class Socket : IDisposable {
58
59                 /*
60                  *      These two fields are looked up by name by the runtime, don't change
61                  *  their name without also updating the runtime code.
62                  */
63                 private static int ipv4Supported = -1, ipv6Supported = -1;
64
65                 static Socket ()
66                 {
67                         // initialize ipv4Supported and ipv6Supported
68                         CheckProtocolSupport ();
69                 }
70
71                 internal static void CheckProtocolSupport ()
72                 {
73                         if(ipv4Supported == -1) {
74                                 try {
75                                         Socket tmp = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
76                                         tmp.Close();
77
78                                         ipv4Supported = 1;
79                                 } catch {
80                                         ipv4Supported = 0;
81                                 }
82                         }
83
84                         if (ipv6Supported == -1) {
85 #if !NET_2_1
86 #if NET_2_0 && CONFIGURATION_DEP
87                                 SettingsSection config;
88                                 config = (SettingsSection) System.Configuration.ConfigurationManager.GetSection ("system.net/settings");
89                                 if (config != null)
90                                         ipv6Supported = config.Ipv6.Enabled ? -1 : 0;
91 #else
92                                 NetConfig config = System.Configuration.ConfigurationSettings.GetConfig("system.net/settings") as NetConfig;
93                                 if (config != null)
94                                         ipv6Supported = config.ipv6Enabled ? -1 : 0;
95 #endif
96 #endif
97                                 if (ipv6Supported != 0) {
98                                         try {
99                                                 Socket tmp = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp);
100                                                 tmp.Close();
101
102                                                 ipv6Supported = 1;
103                                         } catch {
104                                                 ipv6Supported = 0;
105                                         }
106                                 }
107                         }
108                 }
109
110                 public static bool SupportsIPv4 {
111                         get {
112                                 CheckProtocolSupport();
113                                 return ipv4Supported == 1;
114                         }
115                 }
116
117 #if NET_2_0
118                 [ObsoleteAttribute ("Use OSSupportsIPv6 instead")]
119 #endif
120                 public static bool SupportsIPv6 {
121                         get {
122                                 CheckProtocolSupport();
123                                 return ipv6Supported == 1;
124                         }
125                 }
126 #if NET_2_1
127                 public static bool OSSupportsIPv4 {
128                         get {
129                                 NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces ();
130                                 
131                                 foreach (NetworkInterface adapter in nics) {
132                                         if (adapter.Supports (NetworkInterfaceComponent.IPv4))
133                                                 return true;
134                                 }
135                                 return false;
136                         }
137                 }
138 #endif
139 #if NET_2_0
140                 public static bool OSSupportsIPv6 {
141                         get {
142                                 NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces ();
143                                 
144                                 foreach (NetworkInterface adapter in nics) {
145                                         if (adapter.Supports (NetworkInterfaceComponent.IPv6))
146                                                 return true;
147                                 }
148                                 return false;
149                         }
150                 }
151 #endif
152
153                 /* the field "socket" is looked up by name by the runtime */
154                 private IntPtr socket;
155                 private AddressFamily address_family;
156                 private SocketType socket_type;
157                 private ProtocolType protocol_type;
158                 internal bool blocking=true;
159                 Thread blocking_thread;
160 #if NET_2_0
161                 private bool isbound;
162 #endif
163                 /* When true, the socket was connected at the time of
164                  * the last IO operation
165                  */
166                 private bool connected;
167                 /* true if we called Close_internal */
168                 private bool closed;
169                 internal bool disposed;
170
171                 /*
172                  * This EndPoint is used when creating new endpoints. Because
173                  * there are many types of EndPoints possible,
174                  * seed_endpoint.Create(addr) is used for creating new ones.
175                  * As such, this value is set on Bind, SentTo, ReceiveFrom,
176                  * Connect, etc.
177                  */
178                 internal EndPoint seed_endpoint = null;
179
180 #if !TARGET_JVM
181                 // Creates a new system socket, returning the handle
182                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
183                 private extern IntPtr Socket_internal(AddressFamily family,
184                                                       SocketType type,
185                                                       ProtocolType proto,
186                                                       out int error);
187 #endif          
188                 
189                 public Socket(AddressFamily family, SocketType type, ProtocolType proto)
190                 {
191                         address_family=family;
192                         socket_type=type;
193                         protocol_type=proto;
194                         
195                         int error;
196                         
197                         socket = Socket_internal (family, type, proto, out error);
198                         if (error != 0)
199                                 throw new SocketException (error);
200 #if !NET_2_1
201                         SocketDefaults ();
202 #endif
203                 }
204
205                 ~Socket ()
206                 {
207                         Dispose (false);
208                 }
209
210
211                 public AddressFamily AddressFamily {
212                         get { return address_family; }
213                 }
214
215 #if !TARGET_JVM
216                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
217                 private extern static void Blocking_internal(IntPtr socket,
218                                                              bool block,
219                                                              out int error);
220 #endif
221                 public bool Blocking {
222                         get {
223                                 return(blocking);
224                         }
225                         set {
226                                 if (disposed && closed)
227                                         throw new ObjectDisposedException (GetType ().ToString ());
228
229                                 int error;
230                                 
231                                 Blocking_internal (socket, value, out error);
232
233                                 if (error != 0)
234                                         throw new SocketException (error);
235                                 
236                                 blocking=value;
237                         }
238                 }
239
240                 public bool Connected {
241                         get { return connected; }
242                         internal set { connected = value; }
243                 }
244
245                 public ProtocolType ProtocolType {
246                         get { return protocol_type; }
247                 }
248 #if NET_2_0
249                 public bool NoDelay {
250                         get {
251                                 if (disposed && closed)
252                                         throw new ObjectDisposedException (GetType ().ToString ());
253
254                                 ThrowIfUpd ();
255
256                                 return (int)(GetSocketOption (
257                                         SocketOptionLevel.Tcp,
258                                         SocketOptionName.NoDelay)) != 0;
259                         }
260
261                         set {
262                                 if (disposed && closed)
263                                         throw new ObjectDisposedException (GetType ().ToString ());
264
265                                 ThrowIfUpd ();
266
267                                 SetSocketOption (
268                                         SocketOptionLevel.Tcp,
269                                         SocketOptionName.NoDelay, value ? 1 : 0);
270                         }
271                 }
272
273                 public int ReceiveBufferSize {
274                         get {
275                                 if (disposed && closed) {
276                                         throw new ObjectDisposedException (GetType ().ToString ());
277                                 }
278                                 return((int)GetSocketOption (SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer));
279                         }
280                         set {
281                                 if (disposed && closed) {
282                                         throw new ObjectDisposedException (GetType ().ToString ());
283                                 }
284                                 if (value < 0) {
285                                         throw new ArgumentOutOfRangeException ("value", "The value specified for a set operation is less than zero");
286                                 }
287                                 
288                                 SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.ReceiveBuffer, value);
289                         }
290                 }
291
292                 public int SendBufferSize {
293                         get {
294                                 if (disposed && closed) {
295                                         throw new ObjectDisposedException (GetType ().ToString ());
296                                 }
297                                 return((int)GetSocketOption (SocketOptionLevel.Socket, SocketOptionName.SendBuffer));
298                         }
299                         set {
300                                 if (disposed && closed) {
301                                         throw new ObjectDisposedException (GetType ().ToString ());
302                                 }
303                                 if (value < 0) {
304                                         throw new ArgumentOutOfRangeException ("value", "The value specified for a set operation is less than zero");
305                                 }
306                                 
307                                 SetSocketOption (SocketOptionLevel.Socket,
308                                                  SocketOptionName.SendBuffer,
309                                                  value);
310                         }
311                 }
312
313                 public short Ttl {
314                         get {
315                                 if (disposed && closed) {
316                                         throw new ObjectDisposedException (GetType ().ToString ());
317                                 }
318                                 
319                                 short ttl_val;
320                                 
321                                 if (address_family == AddressFamily.InterNetwork) {
322                                         ttl_val = (short)((int)GetSocketOption (SocketOptionLevel.IP, SocketOptionName.IpTimeToLive));
323                                 } else if (address_family == AddressFamily.InterNetworkV6) {
324                                         ttl_val = (short)((int)GetSocketOption (SocketOptionLevel.IPv6, SocketOptionName.HopLimit));
325                                 } else {
326                                         throw new NotSupportedException ("This property is only valid for InterNetwork and InterNetworkV6 sockets");
327                                 }
328                                 
329                                 return(ttl_val);
330                         }
331                         set {
332                                 if (disposed && closed) {
333                                         throw new ObjectDisposedException (GetType ().ToString ());
334                                 }
335                                 
336                                 if (address_family == AddressFamily.InterNetwork) {
337                                         SetSocketOption (SocketOptionLevel.IP, SocketOptionName.IpTimeToLive, value);
338                                 } else if (address_family == AddressFamily.InterNetworkV6) {
339                                         SetSocketOption (SocketOptionLevel.IPv6, SocketOptionName.HopLimit, value);
340                                 } else {
341                                         throw new NotSupportedException ("This property is only valid for InterNetwork and InterNetworkV6 sockets");
342                                 }
343                         }
344                 }
345 #endif
346                 // Returns the remote endpoint details in addr and port
347                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
348                 private extern static SocketAddress RemoteEndPoint_internal(IntPtr socket, out int error);
349
350                 public EndPoint RemoteEndPoint {
351                         get {
352                                 if (disposed && closed)
353                                         throw new ObjectDisposedException (GetType ().ToString ());
354                                 
355                                 /*
356                                  * If the seed EndPoint is null, Connect, Bind,
357                                  * etc has not yet been called. MS returns null
358                                  * in this case.
359                                  */
360                                 if (seed_endpoint == null)
361                                         return null;
362                                 
363                                 SocketAddress sa;
364                                 int error;
365                                 
366                                 sa=RemoteEndPoint_internal(socket, out error);
367
368                                 if (error != 0)
369                                         throw new SocketException (error);
370
371                                 return seed_endpoint.Create (sa);
372                         }
373                 }
374
375                 protected virtual void Dispose (bool explicitDisposing)
376                 {
377                         if (disposed)
378                                 return;
379
380                         disposed = true;
381                         connected = false;
382                         if ((int) socket != -1) {
383                                 int error;
384                                 closed = true;
385                                 IntPtr x = socket;
386                                 socket = (IntPtr) (-1);
387                                 Close_internal (x, out error);
388 #if !NET_2_1
389                                 if (blocking_thread != null) {
390                                         blocking_thread.Abort ();
391                                         blocking_thread = null;
392                                 }
393 #endif
394                                 if (error != 0)
395                                         throw new SocketException (error);
396                         }
397                 }
398
399 #if NET_2_1
400                 public void Dispose ()
401 #else
402                 void IDisposable.Dispose ()
403 #endif
404                 {
405                         Dispose (true);
406                         GC.SuppressFinalize (this);
407                 }
408
409                 // Closes the socket
410                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
411                 private extern static void Close_internal(IntPtr socket, out int error);
412
413                 public void Close ()
414                 {
415                         ((IDisposable) this).Dispose ();
416                 }
417
418                 // Connects to the remote address
419                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
420                 private extern static void Connect_internal(IntPtr sock,
421                                                             SocketAddress sa,
422                                                             out int error);
423
424                 public void Connect (EndPoint remote_end)
425                 {
426                         if (disposed && closed)
427                                 throw new ObjectDisposedException (GetType ().ToString ());
428
429                         if (remote_end == null)
430                                 throw new ArgumentNullException("remote_end");
431
432                         if (remote_end is IPEndPoint) {
433                                 IPEndPoint ep = (IPEndPoint) remote_end;
434                                 if (ep.Address.Equals (IPAddress.Any) || ep.Address.Equals (IPAddress.IPv6Any))
435                                         throw new SocketException ((int) SocketError.AddressNotAvailable);
436                         }
437
438 #if NET_2_1
439                         // Check for SL2.0b1 restrictions
440                         // - tcp only
441                         // - SiteOfOrigin
442                         // - port 4502->4532 + default
443                         if (protocol_type != ProtocolType.Tcp)
444                                 throw new SocketException ((int) SocketError.AccessDenied);
445                         //FIXME: replace 80 by Application.Curent.Host.Source.Port
446                         if (remote_end is IPEndPoint)
447                                 if (((remote_end as IPEndPoint).Port < 4502 || (remote_end as IPEndPoint).Port > 4530) && (remote_end as IPEndPoint).Port != 80)
448                                         throw new SocketException ((int) SocketError.AccessDenied);
449                         else //unsuported endpoint type
450                                 throw new SocketException ((int) SocketError.AccessDenied);
451                         //FIXME: check for Application.Curent.Host.Source.DnsSafeHost
452 #elif NET_2_0
453                         /* TODO: check this for the 1.1 profile too */
454                         if (islistening)
455                                 throw new InvalidOperationException ();
456 #endif
457
458                         SocketAddress serial = remote_end.Serialize ();
459                         int error = 0;
460
461                         blocking_thread = Thread.CurrentThread;
462                         try {
463                                 Connect_internal (socket, serial, out error);
464                         } catch (ThreadAbortException) {
465                                 if (disposed) {
466 #if !NET_2_1 //2.1 profile does not contains Thread.ResetAbort
467                                         Thread.ResetAbort ();
468 #endif
469                                         error = (int) SocketError.Interrupted;
470                                 }
471                         } finally {
472                                 blocking_thread = null;
473                         }
474
475                         if (error != 0)
476                                 throw new SocketException (error);
477
478                         connected=true;
479
480 #if NET_2_0
481                         isbound = true;
482 #endif
483                         
484                         seed_endpoint = remote_end;
485                 }
486
487 #if NET_2_0
488                 public bool ReceiveAsync (SocketAsyncEventArgs e)
489                 {
490                         // NO check is made whether e != null in MS.NET (NRE is thrown in such case)
491                         //
492                         // LAME SPEC: the ArgumentException is never thrown, instead an NRE is
493                         // thrown when e.Buffer and e.BufferList are null (works fine when one is
494                         // set to a valid object)
495                         if (disposed && closed)
496                                 throw new ObjectDisposedException (GetType ().ToString ());
497
498                         // We do not support recv into multiple buffers yet
499                         if (e.BufferList != null)
500                                 throw new NotSupportedException ("Mono doesn't support using BufferList at this point.");
501                         
502                         e.DoOperation (SocketAsyncOperation.Receive, this);
503
504                         // We always return true for now
505                         return true;
506                 }
507
508                 public bool SendAsync (SocketAsyncEventArgs e)
509                 {
510                         // NO check is made whether e != null in MS.NET (NRE is thrown in such case)
511                         
512                         if (disposed && closed)
513                                 throw new ObjectDisposedException (GetType ().ToString ());
514                         if (e.Buffer == null && e.BufferList == null)
515                                 throw new ArgumentException ("Either e.Buffer or e.BufferList must be valid buffers.");
516
517                         e.DoOperation (SocketAsyncOperation.Send, this);
518
519                         // We always return true for now
520                         return true;
521                 }
522 #endif
523
524                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
525                 extern static bool Poll_internal (IntPtr socket, SelectMode mode, int timeout, out int error);
526
527                 /* This overload is needed as the async Connect method
528                  * also needs to check the socket error status, but
529                  * getsockopt(..., SO_ERROR) clears the error.
530                  */
531                 internal bool Poll (int time_us, SelectMode mode, out int socket_error)
532                 {
533                         if (disposed && closed)
534                                 throw new ObjectDisposedException (GetType ().ToString ());
535
536                         if (mode != SelectMode.SelectRead &&
537                             mode != SelectMode.SelectWrite &&
538                             mode != SelectMode.SelectError)
539                                 throw new NotSupportedException ("'mode' parameter is not valid.");
540
541                         int error;
542                         bool result = Poll_internal (socket, mode, time_us, out error);
543                         if (error != 0)
544                                 throw new SocketException (error);
545
546                         socket_error = (int)GetSocketOption (SocketOptionLevel.Socket, SocketOptionName.Error);
547                         
548                         if (mode == SelectMode.SelectWrite && result) {
549                                 /* Update the connected state; for
550                                  * non-blocking Connect()s this is
551                                  * when we can find out that the
552                                  * connect succeeded.
553                                  */
554                                 if (socket_error == 0) {
555                                         connected = true;
556                                 }
557                         }
558                         
559                         return result;
560                 }
561
562                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
563                 private extern static int Receive_internal(IntPtr sock,
564                                                            byte[] buffer,
565                                                            int offset,
566                                                            int count,
567                                                            SocketFlags flags,
568                                                            out int error);
569
570                 internal int Receive_nochecks (byte [] buf, int offset, int size, SocketFlags flags, out SocketError error)
571                 {
572                         int nativeError;
573                         int ret = Receive_internal (socket, buf, offset, size, flags, out nativeError);
574                         error = (SocketError) nativeError;
575                         if (error != SocketError.Success && error != SocketError.WouldBlock && error != SocketError.InProgress)
576                                 connected = false;
577                         else
578                                 connected = true;
579                         
580                         return ret;
581                 }
582
583                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
584                 private extern static void GetSocketOption_obj_internal(IntPtr socket,
585                         SocketOptionLevel level, SocketOptionName name, out object obj_val,
586                         out int error);
587
588                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
589                 private extern static int Send_internal(IntPtr sock,
590                                                         byte[] buf, int offset,
591                                                         int count,
592                                                         SocketFlags flags,
593                                                         out int error);
594
595                 internal int Send_nochecks (byte [] buf, int offset, int size, SocketFlags flags, out SocketError error)
596                 {
597                         if (size == 0) {
598                                 error = SocketError.Success;
599                                 return 0;
600                         }
601
602                         int nativeError;
603
604                         int ret = Send_internal (socket, buf, offset, size, flags, out nativeError);
605
606                         error = (SocketError)nativeError;
607
608                         if (error != SocketError.Success && error != SocketError.WouldBlock && error != SocketError.InProgress)
609                                 connected = false;
610                         else
611                                 connected = true;
612
613                         return ret;
614                 }
615
616                 public object GetSocketOption (SocketOptionLevel level, SocketOptionName name)
617                 {
618                         if (disposed && closed)
619                                 throw new ObjectDisposedException (GetType ().ToString ());
620
621                         object obj_val;
622                         int error;
623                         
624                         GetSocketOption_obj_internal(socket, level, name, out obj_val,
625                                 out error);
626                         if (error != 0)
627                                 throw new SocketException (error);
628                         
629                         if (name == SocketOptionName.Linger) {
630                                 return((LingerOption)obj_val);
631                         } else if (name==SocketOptionName.AddMembership ||
632                                    name==SocketOptionName.DropMembership) {
633                                 return((MulticastOption)obj_val);
634                         } else if (obj_val is int) {
635                                 return((int)obj_val);
636                         } else {
637                                 return(obj_val);
638                         }
639                 }
640
641                 [MethodImplAttribute (MethodImplOptions.InternalCall)]
642                 private extern static void Shutdown_internal (IntPtr socket, SocketShutdown how, out int error);
643                 
644                 public void Shutdown (SocketShutdown how)
645                 {
646                         if (disposed && closed)
647                                 throw new ObjectDisposedException (GetType ().ToString ());
648
649                         int error;
650                         
651                         Shutdown_internal (socket, how, out error);
652
653                         if (error != 0)
654                                 throw new SocketException (error);
655                 }
656
657                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
658                 private extern static void SetSocketOption_internal (IntPtr socket, SocketOptionLevel level,
659                                                                      SocketOptionName name, object obj_val,
660                                                                      byte [] byte_val, int int_val,
661                                                                      out int error);
662
663                 public void SetSocketOption (SocketOptionLevel level, SocketOptionName name, int opt_value)
664                 {
665                         if (disposed && closed)
666                                 throw new ObjectDisposedException (GetType ().ToString ());
667
668                         int error;
669                         
670                         SetSocketOption_internal(socket, level, name, null,
671                                                  null, opt_value, out error);
672
673                         if (error != 0)
674                                 throw new SocketException (error);
675                 }
676
677                 private void ThrowIfUpd ()
678                 {
679 #if !NET_2_1
680                         if (protocol_type == ProtocolType.Udp)
681                                 throw new SocketException ((int)SocketError.ProtocolOption);
682 #endif
683                 }
684         }
685 }
686