2004-01-24 Gonzalo Paniagua Javier <gonzalo@ximian.com>
[mono.git] / mcs / class / System / System.Net.Sockets / Socket.cs
1 // System.Net.Sockets.Socket.cs
2 //
3 // Authors:
4 //    Phillip Pearson (pp@myelin.co.nz)
5 //    Dick Porter <dick@ximian.com>
6 //
7 // Copyright (C) 2001, 2002 Phillip Pearson and Ximian, Inc.
8 //    http://www.myelin.co.nz
9 //
10
11 using System;
12 using System.Net;
13 using System.Collections;
14 using System.Runtime.CompilerServices;
15 using System.Threading;
16 using System.Reflection;
17 using System.IO;
18
19 namespace System.Net.Sockets 
20 {
21         public class Socket : IDisposable 
22         {
23                 private sealed class SocketAsyncResult: IAsyncResult 
24                 {
25                         private object state;
26                         private WaitHandle waithandle;
27                         private bool completed_sync, completed;
28                         private Worker worker;
29                         private Exception delayedException = null;
30
31                         public SocketAsyncResult(object state) {
32                                 this.state=state;
33                                 waithandle=new ManualResetEvent(false);
34                                 completed_sync=completed=false;
35                         }
36
37                         public void SetDelayedException (Exception e) {
38                                 delayedException = e;
39                         }
40
41                         public void CheckIfThrowDelayedException () {
42                                 if (delayedException != null)
43                                         throw delayedException;
44                         }
45
46                         public object AsyncState {
47                                 get {
48                                         return(state);
49                                 }
50                         }
51
52                         public WaitHandle AsyncWaitHandle {
53                                 get {
54                                         if (waithandle == null)
55                                                 waithandle = new ManualResetEvent (completed);
56
57                                         return waithandle;
58                                 }
59                                 set {
60                                         waithandle=value;
61                                 }
62                         }
63
64                         public bool CompletedSynchronously {
65                                 get {
66                                         return(completed_sync);
67                                 }
68                         }
69
70                         public bool IsCompleted {
71                                 get {
72                                         return(completed);
73                                 }
74                                 set {
75                                         completed=value;
76                                         if (waithandle != null && value) {
77                                                 ((ManualResetEvent) waithandle).Set ();
78                                         }
79                                 }
80                         }
81                         
82                         public Worker Worker {
83                                 get {
84                                         return(worker);
85                                 }
86                                 set {
87                                         worker=value;
88                                 }
89                         }
90                 }
91
92                 private sealed class Worker 
93                 {
94                         private AsyncCallback callback;
95                         private SocketAsyncResult result;
96                         private Socket socket;
97
98                         // Parameters
99                         private EndPoint endpoint;      // Connect,ReceiveFrom,SendTo
100                         private byte[] buffer;          // Receive,ReceiveFrom,Send,SendTo
101                         private int offset;             // Receive,ReceiveFrom,Send,SendTo
102                         private int size;               // Receive,ReceiveFrom,Send,SendTo
103                         private SocketFlags sockflags;  // Receive,ReceiveFrom,Send,SendTo
104
105                         // Return values
106                         private Socket acc_socket;
107                         private int total;
108                         
109
110                         // For Accept
111                         public Worker(Socket req_sock,
112                                       AsyncCallback req_callback,
113                                       SocketAsyncResult req_result)
114                                 : this(req_sock, null, 0, 0, SocketFlags.None,
115                                        null, req_callback, req_result) {}
116
117                         // For Connect
118                         public Worker(Socket req_sock, EndPoint req_endpoint,
119                                       AsyncCallback req_callback,
120                                       SocketAsyncResult req_result)
121                                 : this(req_sock, null, 0, 0, SocketFlags.None,
122                                        req_endpoint, req_callback,
123                                        req_result) {}
124
125                         // For Receive and Send
126                         public Worker(Socket req_sock, byte[] req_buffer,
127                                       int req_offset, int req_size,
128                                       SocketFlags req_sockflags,
129                                       AsyncCallback req_callback,
130                                       SocketAsyncResult req_result)
131                                 : this(req_sock, req_buffer, req_offset,
132                                        req_size, req_sockflags, null,
133                                        req_callback, req_result) {}
134
135                         // For ReceiveFrom and SendTo
136                         public Worker(Socket req_sock, byte[] req_buffer,
137                                       int req_offset, int req_size,
138                                       SocketFlags req_sockflags,
139                                       EndPoint req_endpoint,
140                                       AsyncCallback req_callback,
141                                       SocketAsyncResult req_result) {
142                                 socket=req_sock;
143                                 buffer=req_buffer;
144                                 offset=req_offset;
145                                 size=req_size;
146                                 sockflags=req_sockflags;
147                                 endpoint=req_endpoint;
148                                 callback=req_callback;
149                                 result=req_result;
150                         }
151
152                         private void End() {
153                                 result.IsCompleted=true;
154                                 if (callback != null)
155                                         callback(result);
156                         }
157                         
158                         public void Accept() {
159                                 lock(result) {
160                                         try {
161                                                 acc_socket=socket.Accept();
162                                         } catch (Exception e) {
163                                                 result.SetDelayedException(e);
164                                         }
165                                         End();
166                                 }
167                         }
168
169                         public void Connect() {
170                                 lock(result) {
171                                         if (socket.Blocking) {
172                                                 try {
173                                                         socket.Connect(endpoint);
174                                                 } catch (Exception e) {
175                                                         result.SetDelayedException(e);
176                                                 }
177                                                 End ();
178                                                 return;
179                                         }
180
181                                         SocketException rethrow = null;
182                                         try {
183                                                 socket.Connect (endpoint);
184                                         } catch (SocketException e) {
185                                                 //WSAEINPROGRESS
186                                                 if (e.NativeErrorCode != 10036) {
187                                                         result.SetDelayedException(e);  
188                                                         End ();
189                                                         return;
190                                                 }
191
192                                                 socket.Poll (-1, SelectMode.SelectWrite);
193                                                 try {
194                                                         socket.Connect (endpoint);
195                                                 } catch (SocketException e2) {
196                                                         rethrow = e2;
197                                                 }
198                                         }
199                                         if (rethrow != null)
200                                                 result.SetDelayedException(rethrow);
201                                         End ();
202                                 }
203                         }
204
205                         public void Receive() {
206                                 lock(result) {
207                                         if (socket.Blocking) {
208                                                 try {
209                                                         total=socket.Receive(buffer, offset,
210                                                                              size, sockflags);
211                                                 } catch (Exception e) {
212                                                         result.SetDelayedException(e);
213                                                 }
214                                                 End();
215                                                 return;
216                                         }
217
218                                         SocketException rethrow = null;
219                                         try {
220                                                 total = socket.Receive (buffer, offset, size, sockflags);
221                                         } catch (SocketException e) {
222                                                 //WSAEWOULDBLOCK
223                                                 if (e.NativeErrorCode != 10035) {
224                                                         result.SetDelayedException(e);
225                                                         End ();
226                                                         return;
227                                                 }
228
229                                                 socket.Poll (-1, SelectMode.SelectRead);
230                                                 try {
231                                                         total = socket.Receive (buffer, offset, size, sockflags);
232                                                 } catch (SocketException e2) {
233                                                         rethrow = e2;
234                                                 }
235                                         }
236                                         if (rethrow != null)
237                                                 result.SetDelayedException(rethrow);
238                                         End ();
239                                 }
240                         }
241
242                         public void ReceiveFrom() {
243                                 lock(result) {
244                                         if (socket.Blocking) {
245                                                 try {
246                                                         total=socket.ReceiveFrom(buffer,
247                                                                                  offset, size,
248                                                                                  sockflags,
249                                                                                  ref endpoint);
250                                                 } catch (Exception e) {
251                                                         result.SetDelayedException(e);
252                                                 }
253                                                 End();
254                                                 return;
255                                         }
256
257                                         SocketException rethrow = null;
258                                         try {
259                                                 total = socket.ReceiveFrom (buffer, offset, size,
260                                                                         sockflags, ref endpoint);
261                                         } catch (SocketException e) {
262                                                 //WSAEWOULDBLOCK
263                                                 if (e.NativeErrorCode != 10035) {
264                                                         result.SetDelayedException(e);
265                                                         End ();
266                                                         return;
267                                                 }
268
269                                                 socket.Poll (-1, SelectMode.SelectRead);
270                                                 try {
271                                                         total = socket.ReceiveFrom (buffer, offset, size,
272                                                                                 sockflags, ref endpoint);
273                                                 } catch (SocketException e2) {
274                                                         rethrow = e2;
275                                                 }
276                                         }
277                                         if (rethrow != null)
278                                                 result.SetDelayedException(rethrow);
279                                         End ();
280                                 }
281                         }
282
283                         public void Send() {
284                                 lock(result) {
285                                         if (socket.Blocking) {
286                                                 try {
287                                                         total=socket.Send(buffer, offset, size,
288                                                                           sockflags);
289                                                 } catch (Exception e) {
290                                                         result.SetDelayedException(e);
291                                                 }
292                                                 End();
293                                                 return;
294                                         }
295
296                                         SocketException rethrow = null;
297                                         try {
298                                                 total = socket.Send (buffer, offset, size, sockflags);
299                                         } catch (SocketException e) {
300                                                 //WSAEWOULDBLOCK
301                                                 if (e.NativeErrorCode != 10035) {
302                                                         result.SetDelayedException(e);
303                                                         End ();
304                                                         return;
305                                                 }
306
307                                                 socket.Poll (-1, SelectMode.SelectWrite);
308                                                 try {
309                                                         total = socket.Send (buffer, offset, size, sockflags);
310                                                 } catch (SocketException e2) {
311                                                         rethrow = e2;
312                                                 }
313                                         }
314
315                                         if (rethrow != null)
316                                                 result.SetDelayedException(rethrow);
317                                         End ();
318                                 }
319                         }
320
321                         public void SendTo() {
322                                 lock(result) {
323                                         if (socket.Blocking) {
324                                                 try {
325                                                         total=socket.SendTo(buffer, offset,
326                                                                             size, sockflags,
327                                                                             endpoint);
328                                                 } catch (Exception e) {
329                                                         result.SetDelayedException(e);
330                                                 }
331                                                 End();
332                                                 return;
333                                         }
334
335                                         SocketException rethrow = null;
336                                         try {
337                                                 total = socket.SendTo (buffer, offset, size,
338                                                                         sockflags, endpoint);
339                                         } catch (SocketException e) {
340                                                 //WSAEWOULDBLOCK
341                                                 if (e.NativeErrorCode != 10035) {
342                                                         result.SetDelayedException(e);
343                                                         End ();
344                                                         return;
345                                                 }
346
347                                                 socket.Poll (-1, SelectMode.SelectWrite);
348                                                 try {
349                                                         total = socket.SendTo (buffer, offset, size,
350                                                                                 sockflags, endpoint);
351                                                 } catch (SocketException e2) {
352                                                         rethrow = e2;
353                                                 }
354                                         }
355
356                                         if (rethrow != null)
357                                                 result.SetDelayedException(rethrow);
358                                         End ();
359                                 }
360                         }
361
362                         public EndPoint EndPoint {
363                                 get {
364                                         return(endpoint);
365                                 }
366                         }
367
368                         public Socket Socket {
369                                 get {
370                                         return(acc_socket);
371                                 }
372                         }
373
374                         public int Total {
375                                 get {
376                                         return(total);
377                                 }
378                         }
379                 }
380                         
381                 /* the field "socket" is looked up by name by the runtime */
382                 private IntPtr socket;
383                 private AddressFamily address_family;
384                 private SocketType socket_type;
385                 private ProtocolType protocol_type;
386                 private bool blocking=true;
387                 private int pendingEnds;
388                 private int closeDelayed;
389 \r
390                 /*\r
391                  *      These two fields are looked up by name by the runtime, don't change\r
392                  *  their name without also updating the runtime code.\r
393                  */
394                 private static int ipv4Supported = -1, ipv6Supported = -1;
395
396                 /* When true, the socket was connected at the time of
397                  * the last IO operation
398                  */
399                 private bool connected=false;
400                 /* true if we called Close_internal */
401                 private bool closed;
402
403                 /* Used in LocalEndPoint and RemoteEndPoint if the
404                  * Mono.Posix assembly is available
405                  */
406                 private static object unixendpoint=null;
407                 private static Type unixendpointtype=null;
408                 
409                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
410                 private extern static void Select_internal(ref Socket[] read,
411                                                            ref Socket[] write,
412                                                            ref Socket[] err,
413                                                            int timeout);
414
415                 public static void Select(IList read_list, IList write_list,
416                                           IList err_list, int time_us) {
417                         if(read_list==null &&
418                            write_list==null &&
419                            err_list==null) {
420                                 throw new ArgumentNullException();
421                         }
422
423                         int read_count = 0, write_count = 0, err_count = 0;
424                         Socket[] read_arr = null;
425                         Socket[] write_arr = null;
426                         Socket[] err_arr = null;
427
428                         if (read_list!=null)
429                                 read_count=read_list.Count;
430
431                         if (read_count != 0)
432                                 read_arr=new Socket[read_count];
433
434                         if (write_list!=null)
435                                 write_count=write_list.Count;
436
437                         if (write_count != 0)
438                                 write_arr=new Socket[write_count];
439
440                         if (err_list!=null)
441                                 err_count=err_list.Count;
442
443                         if (err_count != 0)
444                                 err_arr=new Socket[err_count];
445                         
446                         int i;
447
448                         if (read_count != 0) {
449                                 i=0;
450                                 
451                                 foreach (Socket s in read_list) {
452                                         read_arr[i]=s;
453                                         i++;
454                                 }
455                         }
456
457                         if (write_count != 0) {
458                                 i=0;
459                                 foreach (Socket s in write_list) {
460                                         write_arr[i]=s;
461                                         i++;
462                                 }
463                         }
464                         
465                         if (err_count != 0) {
466                                 i=0;
467                                 foreach (Socket s in err_list) {
468                                         err_arr[i]=s;
469                                         i++;
470                                 }
471                         }
472
473                         Select_internal(ref read_arr, ref write_arr,
474                                         ref err_arr, time_us);
475
476                         if(read_list!=null) {
477                                 read_list.Clear();
478                                 for(i=0; i<read_arr.Length; i++) {
479                                         read_list.Add(read_arr[i]);
480                                 }
481                         }
482                         
483                         if(write_list!=null) {
484                                 write_list.Clear();
485                                 for(i=0; i<write_arr.Length; i++) {
486                                         write_list.Add(write_arr[i]);
487                                 }
488                         }
489                         
490                         if(err_list!=null) {
491                                 err_list.Clear();
492                                 for(i=0; i<err_arr.Length; i++) {
493                                         err_list.Add(err_arr[i]);
494                                 }
495                         }
496                 }
497
498                 static Socket() {
499                         Assembly ass;
500                         
501                         try {
502                                 ass=Assembly.Load("Mono.Posix");
503                         } catch (FileNotFoundException) {
504                                 return;
505                         }
506                                 
507                         unixendpointtype=ass.GetType("Mono.Posix.UnixEndPoint");
508
509                         /* The endpoint Create() method is an instance
510                          * method :-(
511                          */
512                         Type[] arg_types=new Type[1];
513                         arg_types[0]=typeof(string);
514                         ConstructorInfo cons=unixendpointtype.GetConstructor(arg_types);
515
516                         object[] args=new object[1];
517                         args[0]="";
518
519                         unixendpoint=cons.Invoke(args);
520                 }
521
522                 // private constructor used by Accept, which already
523                 // has a socket handle to use
524                 private Socket(AddressFamily family, SocketType type,
525                                ProtocolType proto, IntPtr sock) {
526                         address_family=family;
527                         socket_type=type;
528                         protocol_type=proto;
529                         
530                         socket=sock;
531                         connected=true;
532                 }
533                 
534                 // Creates a new system socket, returning the handle
535                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
536                 private extern IntPtr Socket_internal(AddressFamily family,
537                                                       SocketType type,
538                                                       ProtocolType proto);
539                 
540                 public Socket(AddressFamily family, SocketType type,
541                               ProtocolType proto) {
542                         address_family=family;
543                         socket_type=type;
544                         protocol_type=proto;
545                         
546                         socket=Socket_internal(family, type, proto);
547                 }
548
549                 public AddressFamily AddressFamily {
550                         get {
551                                 return(address_family);
552                         }
553                 }
554
555                 // Returns the amount of data waiting to be read on socket
556                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
557                 private extern static int Available_internal(IntPtr socket);
558                 
559                 public int Available {
560                         get {
561                                 if (disposed && closed)
562                                         throw new ObjectDisposedException (GetType ().ToString ());
563
564                                 return(Available_internal(socket));
565                         }
566                 }
567
568                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
569                 private extern static void Blocking_internal(IntPtr socket,
570                                                             bool block);
571
572                 public bool Blocking {
573                         get {
574                                 return(blocking);
575                         }
576                         set {
577                                 Blocking_internal(socket, value);
578                                 blocking=value;
579                         }
580                 }
581
582                 public bool Connected {
583                         get {
584                                 return(connected);
585                         }
586                 }
587
588                 public IntPtr Handle {
589                         get {
590                                 return(socket);
591                         }
592                 }
593
594                 // Returns the local endpoint details in addr and port
595                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
596                 private extern static SocketAddress LocalEndPoint_internal(IntPtr socket);
597
598                 [MonoTODO("Support non-IP endpoints")]
599                 public EndPoint LocalEndPoint {
600                         get {
601                                 if (disposed && closed)
602                                         throw new ObjectDisposedException (GetType ().ToString ());
603
604                                 SocketAddress sa;
605                                 
606                                 sa=LocalEndPoint_internal(socket);
607
608                                 if(sa.Family==AddressFamily.InterNetwork || sa.Family==AddressFamily.InterNetworkV6) {
609                                         // Stupidly, EndPoint.Create() is an
610                                         // instance method
611                                         return new IPEndPoint(0, 0).Create(sa);
612                                 } else if (sa.Family==AddressFamily.Unix &&
613                                            unixendpoint!=null) {
614                                         return((EndPoint)unixendpointtype.InvokeMember("Create", BindingFlags.InvokeMethod|BindingFlags.Instance|BindingFlags.Public, null, unixendpoint, new object[] {sa}));
615                                 } else {
616                                         throw new NotImplementedException();
617                                 }
618                         }
619                 }
620
621                 public ProtocolType ProtocolType {
622                         get {
623                                 return(protocol_type);
624                         }
625                 }
626
627                 // Returns the remote endpoint details in addr and port
628                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
629                 private extern static SocketAddress RemoteEndPoint_internal(IntPtr socket);
630
631                 [MonoTODO("Support non-IP endpoints")]
632                 public EndPoint RemoteEndPoint {
633                         get {
634                                 if (disposed && closed)
635                                         throw new ObjectDisposedException (GetType ().ToString ());
636
637                                 SocketAddress sa;
638                                 
639                                 sa=RemoteEndPoint_internal(socket);
640
641                                 if(sa.Family==AddressFamily.InterNetwork || sa.Family==AddressFamily.InterNetworkV6 ) {
642                                         // Stupidly, EndPoint.Create() is an
643                                         // instance method
644                                         return new IPEndPoint(0, 0).Create(sa);
645                                 } else if (sa.Family==AddressFamily.Unix &&
646                                            unixendpoint!=null) {
647                                         return((EndPoint)unixendpointtype.InvokeMember("Create", BindingFlags.InvokeMethod|BindingFlags.Instance|BindingFlags.Public, null, unixendpoint, new object[] {sa}));
648                                 } else {
649                                         throw new NotImplementedException();
650                                 }
651                         }
652                 }
653
654                 public SocketType SocketType {
655                         get {
656                                 return(socket_type);
657                         }
658                 }
659
660 #if NET_1_1
661                 public static bool SupportsIPv4 {\r
662                         get {\r
663                                 CheckProtocolSupport();\r
664                                 return ipv4Supported == 1;\r
665                         }\r
666                 }
667
668                 public static bool SupportsIPv6 {\r
669                         get {\r
670                                 CheckProtocolSupport();\r
671                                 return ipv6Supported == 1;\r
672                         }\r
673                 }
674 #else
675                 internal static bool SupportsIPv4 \r
676                 {\r
677                         get \r
678                         {\r
679                                 return true;\r
680                         }\r
681                 }
682
683                 internal static bool SupportsIPv6 \r
684                 {\r
685                         get \r
686                         {\r
687                                 return false;\r
688                         }\r
689                 }\r
690 #endif
691
692                 internal static void CheckProtocolSupport()
693                 {\r
694                         if(ipv4Supported == -1) {\r
695                                 try  {\r
696                                         Socket tmp = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);\r
697                                         tmp.Close();\r
698 \r
699                                         ipv4Supported = 1;\r
700                                 }\r
701                                 catch {\r
702                                         ipv4Supported = 0;\r
703                                 }\r
704                         }\r
705 \r
706                         if(ipv6Supported == -1) {\r
707                                 NetConfig config = (NetConfig)System.Configuration.ConfigurationSettings.GetConfig("system.net/settings");\r
708 \r
709                                 if(config != null)\r
710                                         ipv6Supported = config.ipv6Enabled?-1:0;\r
711 \r
712                                 if(ipv6Supported != 0) {\r
713                                         try {\r
714                                                 Socket tmp = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp);\r
715                                                 tmp.Close();\r
716 \r
717                                                 ipv6Supported = 1;\r
718                                         }\r
719                                         catch { }\r
720                                 }\r
721                         }\r
722                 }
723
724                 // Creates a new system socket, returning the handle
725                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
726                 private extern static IntPtr Accept_internal(IntPtr sock);
727                 
728                 public Socket Accept() {
729                         if (disposed && closed)
730                                 throw new ObjectDisposedException (GetType ().ToString ());
731
732                         IntPtr sock=Accept_internal(socket);
733                         
734                         return(new Socket(this.AddressFamily, this.SocketType,
735                                           this.ProtocolType, sock));
736                 }
737
738                 public IAsyncResult BeginAccept(AsyncCallback callback,
739                                                 object state) {
740
741                         if (disposed && closed)
742                                 throw new ObjectDisposedException (GetType ().ToString ());
743
744                         Interlocked.Increment (ref pendingEnds);
745                         SocketAsyncResult req=new SocketAsyncResult(state);
746                         Worker worker=new Worker(this, callback, req);
747                         req.Worker=worker;
748                         Thread child=new Thread(new ThreadStart(worker.Accept));
749                         child.IsBackground = true;
750                         child.Start();
751                         return(req);
752                 }
753
754                 public IAsyncResult BeginConnect(EndPoint end_point,
755                                                  AsyncCallback callback,
756                                                  object state) {
757
758                         if (disposed && closed)
759                                 throw new ObjectDisposedException (GetType ().ToString ());
760
761                         if (end_point == null)
762                                 throw new ArgumentNullException ("end_point");
763
764                         Interlocked.Increment (ref pendingEnds);
765                         SocketAsyncResult req=new SocketAsyncResult(state);
766                         Worker worker=new Worker(this, end_point, callback,
767                                                  req);
768                         req.Worker=worker;
769                         Thread child=new Thread(new ThreadStart(worker.Connect));
770                         child.IsBackground = true;
771                         child.Start();
772                         return(req);
773                 }
774
775                 public IAsyncResult BeginReceive(byte[] buffer, int offset,
776                                                  int size,
777                                                  SocketFlags socket_flags,
778                                                  AsyncCallback callback,
779                                                  object state) {
780
781                         if (disposed && closed)
782                                 throw new ObjectDisposedException (GetType ().ToString ());
783
784                         if (buffer == null)
785                                 throw new ArgumentNullException ("buffer");
786
787                         if (offset < 0)
788                                 throw new ArgumentOutOfRangeException ("offset must be >= 0");
789
790                         if (size < 0)
791                                 throw new ArgumentOutOfRangeException ("size must be >= 0");
792
793                         if (offset + size > buffer.Length)
794                                 throw new ArgumentOutOfRangeException ("offset + size exceeds the buffer length");
795
796                         Interlocked.Increment (ref pendingEnds);
797                         SocketAsyncResult req=new SocketAsyncResult(state);
798                         Worker worker=new Worker(this, buffer, offset, size,
799                                                  socket_flags, callback, req);
800                         req.Worker=worker;
801                         Thread child=new Thread(new ThreadStart(worker.Receive));
802                         child.IsBackground = true;
803                         child.Start();
804                         return(req);
805                 }
806
807                 public IAsyncResult BeginReceiveFrom(byte[] buffer, int offset,
808                                                      int size,
809                                                      SocketFlags socket_flags,
810                                                      ref EndPoint remote_end,
811                                                      AsyncCallback callback,
812                                                      object state) {
813                         if (disposed && closed)
814                                 throw new ObjectDisposedException (GetType ().ToString ());
815
816                         if (buffer == null)
817                                 throw new ArgumentNullException ("buffer");
818
819                         if (offset < 0)
820                                 throw new ArgumentOutOfRangeException ("offset must be >= 0");
821
822                         if (size < 0)
823                                 throw new ArgumentOutOfRangeException ("size must be >= 0");
824
825                         if (offset + size > buffer.Length)
826                                 throw new ArgumentOutOfRangeException ("offset + size exceeds the buffer length");
827
828                         Interlocked.Increment (ref pendingEnds);
829                         SocketAsyncResult req=new SocketAsyncResult(state);
830                         Worker worker=new Worker(this, buffer, offset, size,
831                                                  socket_flags, remote_end,
832                                                  callback, req);
833                         req.Worker=worker;
834                         Thread child=new Thread(new ThreadStart(worker.ReceiveFrom));
835                         child.IsBackground = true;
836                         child.Start();
837                         return(req);
838                 }
839
840                 public IAsyncResult BeginSend(byte[] buffer, int offset,
841                                               int size,
842                                               SocketFlags socket_flags,
843                                               AsyncCallback callback,
844                                               object state) {
845                         if (disposed && closed)
846                                 throw new ObjectDisposedException (GetType ().ToString ());
847
848                         if (buffer == null)
849                                 throw new ArgumentNullException ("buffer");
850
851                         if (offset < 0)
852                                 throw new ArgumentOutOfRangeException ("offset must be >= 0");
853
854                         if (size < 0)
855                                 throw new ArgumentOutOfRangeException ("size must be >= 0");
856
857                         if (offset + size > buffer.Length)
858                                 throw new ArgumentOutOfRangeException ("offset + size exceeds the buffer length");
859
860                         Interlocked.Increment (ref pendingEnds);
861                         SocketAsyncResult req=new SocketAsyncResult(state);
862                         Worker worker=new Worker(this, buffer, offset, size,
863                                                  socket_flags, callback, req);
864                         req.Worker=worker;
865                         Thread child=new Thread(new ThreadStart(worker.Send));
866                         child.IsBackground = true;
867                         child.Start();
868                         return(req);
869                 }
870
871                 public IAsyncResult BeginSendTo(byte[] buffer, int offset,
872                                                 int size,
873                                                 SocketFlags socket_flags,
874                                                 EndPoint remote_end,
875                                                 AsyncCallback callback,
876                                                 object state) {
877                         if (disposed && closed)
878                                 throw new ObjectDisposedException (GetType ().ToString ());
879
880                         if (buffer == null)
881                                 throw new ArgumentNullException ("buffer");
882
883                         if (offset < 0)
884                                 throw new ArgumentOutOfRangeException ("offset must be >= 0");
885
886                         if (size < 0)
887                                 throw new ArgumentOutOfRangeException ("size must be >= 0");
888
889                         if (offset + size > buffer.Length)
890                                 throw new ArgumentOutOfRangeException ("offset + size exceeds the buffer length");
891
892                         Interlocked.Increment (ref pendingEnds);
893                         SocketAsyncResult req=new SocketAsyncResult(state);
894                         Worker worker=new Worker(this, buffer, offset, size,
895                                                  socket_flags, remote_end,
896                                                  callback, req);
897                         req.Worker=worker;
898                         Thread child=new Thread(new ThreadStart(worker.SendTo));
899                         child.IsBackground = true;
900                         child.Start();
901                         return(req);
902                 }
903
904                 // Creates a new system socket, returning the handle
905                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
906                 private extern static void Bind_internal(IntPtr sock,
907                                                          SocketAddress sa);
908
909                 public void Bind(EndPoint local_end) {
910                         if (disposed && closed)
911                                 throw new ObjectDisposedException (GetType ().ToString ());
912
913                         if(local_end==null) {
914                                 throw new ArgumentNullException("local_end");
915                         }
916                         
917                         Bind_internal(socket, local_end.Serialize());
918                 }
919
920                 // Closes the socket
921                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
922                 private extern static void Close_internal(IntPtr socket);
923                 
924                 public void Close() {
925                         ((IDisposable) this).Dispose ();
926                 }
927
928                 // Connects to the remote address
929                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
930                 private extern static void Connect_internal(IntPtr sock,
931                                                             SocketAddress sa);
932
933                 public void Connect(EndPoint remote_end) {
934                         if (disposed && closed)
935                                 throw new ObjectDisposedException (GetType ().ToString ());
936
937                         if(remote_end==null) {
938                                 throw new ArgumentNullException("remote_end");
939                         }
940
941                         Connect_internal(socket, remote_end.Serialize());
942                         connected=true;
943                 }
944                 
945                 public Socket EndAccept(IAsyncResult result) {
946                         if (disposed && closed)
947                                 throw new ObjectDisposedException (GetType ().ToString ());
948
949                         if (result == null)
950                                 throw new ArgumentNullException ("result");
951
952                         SocketAsyncResult req = result as SocketAsyncResult;
953                         if (req == null)
954                                 throw new ArgumentException ("Invalid IAsyncResult");
955
956                         if (!result.IsCompleted)
957                                 result.AsyncWaitHandle.WaitOne();
958
959                         Interlocked.Decrement (ref pendingEnds);
960                         CheckIfClose ();
961                         req.CheckIfThrowDelayedException();
962                         return(req.Worker.Socket);
963                 }
964
965                 public void EndConnect(IAsyncResult result) {
966                         if (disposed && closed)
967                                 throw new ObjectDisposedException (GetType ().ToString ());
968
969                         if (result == null)
970                                 throw new ArgumentNullException ("result");
971
972                         SocketAsyncResult req = result as SocketAsyncResult;
973                         if (req == null)
974                                 throw new ArgumentException ("Invalid IAsyncResult");
975
976                         if (!result.IsCompleted)
977                                 result.AsyncWaitHandle.WaitOne();
978
979                         Interlocked.Decrement (ref pendingEnds);
980                         CheckIfClose ();
981                         req.CheckIfThrowDelayedException();
982                 }
983
984                 public int EndReceive(IAsyncResult result) {
985                         if (disposed && closed)
986                                 throw new ObjectDisposedException (GetType ().ToString ());
987
988                         if (result == null)
989                                 throw new ArgumentNullException ("result");
990
991                         SocketAsyncResult req = result as SocketAsyncResult;
992                         if (req == null)
993                                 throw new ArgumentException ("Invalid IAsyncResult");
994
995                         if (!result.IsCompleted)
996                                 result.AsyncWaitHandle.WaitOne();
997
998                         Interlocked.Decrement (ref pendingEnds);
999                         CheckIfClose ();
1000                         req.CheckIfThrowDelayedException();
1001                         return(req.Worker.Total);
1002                 }
1003
1004                 public int EndReceiveFrom(IAsyncResult result,
1005                                           ref EndPoint end_point) {
1006                         if (disposed && closed)
1007                                 throw new ObjectDisposedException (GetType ().ToString ());
1008
1009                         if (result == null)
1010                                 throw new ArgumentNullException ("result");
1011
1012                         SocketAsyncResult req = result as SocketAsyncResult;
1013                         if (req == null)
1014                                 throw new ArgumentException ("Invalid IAsyncResult");
1015
1016                         if (!result.IsCompleted)
1017                                 result.AsyncWaitHandle.WaitOne();
1018
1019                         Interlocked.Decrement (ref pendingEnds);
1020                         CheckIfClose ();
1021                         req.CheckIfThrowDelayedException();
1022                         end_point=req.Worker.EndPoint;
1023                         return(req.Worker.Total);
1024                 }
1025
1026                 public int EndSend(IAsyncResult result) {
1027                         if (disposed && closed)
1028                                 throw new ObjectDisposedException (GetType ().ToString ());
1029
1030                         if (result == null)
1031                                 throw new ArgumentNullException ("result");
1032
1033                         SocketAsyncResult req = result as SocketAsyncResult;
1034                         if (req == null)
1035                                 throw new ArgumentException ("Invalid IAsyncResult");
1036
1037                         if (!result.IsCompleted)
1038                                 result.AsyncWaitHandle.WaitOne();
1039
1040                         Interlocked.Decrement (ref pendingEnds);
1041                         CheckIfClose ();
1042                         req.CheckIfThrowDelayedException();
1043                         return(req.Worker.Total);
1044                 }
1045
1046                 public int EndSendTo(IAsyncResult result) {
1047                         if (disposed && closed)
1048                                 throw new ObjectDisposedException (GetType ().ToString ());
1049
1050                         if (result == null)
1051                                 throw new ArgumentNullException ("result");
1052
1053                         SocketAsyncResult req = result as SocketAsyncResult;
1054                         if (req == null)
1055                                 throw new ArgumentException ("Invalid IAsyncResult");
1056
1057                         if (!result.IsCompleted)
1058                                 result.AsyncWaitHandle.WaitOne();
1059
1060                         Interlocked.Decrement (ref pendingEnds);
1061                         CheckIfClose ();
1062                         req.CheckIfThrowDelayedException();
1063                         return(req.Worker.Total);
1064                 }
1065
1066                 void CheckIfClose ()
1067                 {
1068                         if (Interlocked.CompareExchange (ref closeDelayed, 0, 1) == 1 &&
1069                             Interlocked.CompareExchange (ref pendingEnds, 0, 0) == 0) {
1070                                 closed = true;
1071                                 Close_internal(socket);
1072                         }
1073                 }
1074
1075                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1076                 private extern static void GetSocketOption_obj_internal(IntPtr socket, SocketOptionLevel level, SocketOptionName name, out object obj_val);
1077                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1078                 private extern static void GetSocketOption_arr_internal(IntPtr socket, SocketOptionLevel level, SocketOptionName name, ref byte[] byte_val);
1079
1080                 public object GetSocketOption(SocketOptionLevel level,
1081                                               SocketOptionName name) {
1082                         object obj_val;
1083                         
1084                         GetSocketOption_obj_internal(socket, level, name,
1085                                                      out obj_val);
1086                         
1087                         if(name==SocketOptionName.Linger) {
1088                                 return((LingerOption)obj_val);
1089                         } else if (name==SocketOptionName.AddMembership ||
1090                                    name==SocketOptionName.DropMembership) {
1091                                 return((MulticastOption)obj_val);
1092                         } else {
1093                                 return((int)obj_val);
1094                         }
1095                 }
1096
1097                 public void GetSocketOption(SocketOptionLevel level,
1098                                             SocketOptionName name,
1099                                             byte[] opt_value) {
1100                         int opt_value_len=opt_value.Length;
1101                         
1102                         GetSocketOption_arr_internal(socket, level, name,
1103                                                      ref opt_value);
1104                 }
1105
1106                 public byte[] GetSocketOption(SocketOptionLevel level,
1107                                               SocketOptionName name,
1108                                               int length) {
1109                         byte[] byte_val=new byte[length];
1110                         
1111                         GetSocketOption_arr_internal(socket, level, name,
1112                                                      ref byte_val);
1113
1114                         return(byte_val);
1115                 }
1116
1117                 [MonoTODO("Totally undocumented")]
1118                 public int IOControl(int ioctl_code, byte[] in_value,
1119                                      byte[] out_value) {
1120                         throw new NotImplementedException();
1121                 }
1122
1123                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1124                 private extern static void Listen_internal(IntPtr sock,
1125                                                            int backlog);
1126
1127                 public void Listen(int backlog) {
1128                         Listen_internal(socket, backlog);
1129                 }
1130
1131                 /* The docs for Poll() are a bit lightweight too, but
1132                  * it seems to be just a simple wrapper around Select.
1133                  */
1134                 public bool Poll(int time_us, SelectMode mode) {
1135                         Socket [] socketlist = new Socket []{this};
1136                         Socket [] n = null;
1137
1138                         switch(mode) {
1139                         case SelectMode.SelectError:
1140                                 Select_internal (ref n, ref n, ref socketlist, time_us);
1141                                 break;
1142                         case SelectMode.SelectRead:
1143                                 Select_internal (ref socketlist, ref n, ref n, time_us);
1144                                 break;
1145                         case SelectMode.SelectWrite:
1146                                 Select_internal (ref n, ref socketlist, ref n, time_us);
1147                                 break;
1148                         default:
1149                                 throw new NotSupportedException();
1150                         }
1151
1152                         return (socketlist.Length == 1);
1153                 }
1154                 
1155                 public int Receive(byte[] buf) {
1156                         return(Receive(buf, 0, buf.Length, SocketFlags.None));
1157                 }
1158
1159                 public int Receive(byte[] buf, SocketFlags flags) {
1160                         return(Receive(buf, 0, buf.Length, flags));
1161                 }
1162
1163                 public int Receive(byte[] buf, int size, SocketFlags flags) {
1164                         return(Receive(buf, 0, size, flags));
1165                 }
1166
1167                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1168                 private extern static int Receive_internal(IntPtr sock,
1169                                                            byte[] buffer,
1170                                                            int offset,
1171                                                            int count,
1172                                                            SocketFlags flags);
1173
1174                 public int Receive(byte[] buf, int offset, int size,
1175                                    SocketFlags flags) {
1176                         if(buf==null) {
1177                                 throw new ArgumentNullException("buffer is null");
1178                         }
1179                         if(offset<0 || offset > buf.Length) {
1180                                 throw new ArgumentOutOfRangeException("offset exceeds the size of buffer");
1181                         }
1182                         if(size<0 || offset+size > buf.Length) {
1183                                 throw new ArgumentOutOfRangeException("offset+size exceeds the size of buffer");
1184                         }
1185                         
1186                         int ret;
1187                         
1188                         try {
1189                                 ret=Receive_internal(socket, buf, offset,
1190                                                      size, flags);
1191                         } catch(SocketException) {
1192                                 connected=false;
1193                                 throw;
1194                         }
1195                         connected=true;
1196
1197                         return(ret);
1198                 }
1199                 
1200                 public int ReceiveFrom(byte[] buf, ref EndPoint remote_end) {
1201                         return(ReceiveFrom(buf, 0, buf.Length,
1202                                            SocketFlags.None, ref remote_end));
1203                 }
1204
1205                 public int ReceiveFrom(byte[] buf, SocketFlags flags,
1206                                        ref EndPoint remote_end) {
1207                         return(ReceiveFrom(buf, 0, buf.Length, flags,
1208                                            ref remote_end));
1209                 }
1210
1211                 public int ReceiveFrom(byte[] buf, int size, SocketFlags flags,
1212                                        ref EndPoint remote_end) {
1213                         return(ReceiveFrom(buf, 0, size, flags,
1214                                            ref remote_end));
1215                 }
1216
1217
1218                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1219                 private extern static int RecvFrom_internal(IntPtr sock,
1220                                                             byte[] buffer,
1221                                                             int offset,
1222                                                             int count,
1223                                                             SocketFlags flags,
1224                                                             ref SocketAddress sockaddr);
1225
1226                 public int ReceiveFrom(byte[] buf, int offset, int size,
1227                                        SocketFlags flags,
1228                                        ref EndPoint remote_end) {
1229                         if(buf==null) {
1230                                 throw new ArgumentNullException("buffer is null");
1231                         }
1232                         if(remote_end==null) {
1233                                 throw new ArgumentNullException("remote endpoint is null");
1234                         }
1235                         if(offset<0 || offset>buf.Length) {
1236                                 throw new ArgumentOutOfRangeException("offset exceeds the size of buffer");
1237                         }
1238                         if(size<0 || offset+size>buf.Length) {
1239                                 throw new ArgumentOutOfRangeException("offset+size exceeds the size of buffer");
1240                         }
1241
1242                         SocketAddress sockaddr=remote_end.Serialize();
1243                         int count;
1244
1245                         try {
1246                                 count=RecvFrom_internal(socket, buf, offset,
1247                                                         size, flags,
1248                                                         ref sockaddr);
1249                         } catch(SocketException) {
1250                                 connected=false;
1251                                 throw;
1252                         }
1253                         connected=true;
1254                         
1255                         // Stupidly, EndPoint.Create() is an
1256                         // instance method
1257                         remote_end=remote_end.Create(sockaddr);
1258
1259                         return(count);
1260                 }
1261
1262                 public int Send(byte[] buf) {
1263                         return(Send(buf, 0, buf.Length, SocketFlags.None));
1264                 }
1265
1266                 public int Send(byte[] buf, SocketFlags flags) {
1267                         return(Send(buf, 0, buf.Length, flags));
1268                 }
1269
1270                 public int Send(byte[] buf, int size, SocketFlags flags) {
1271                         return(Send(buf, 0, size, flags));
1272                 }
1273
1274                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1275                 private extern static int Send_internal(IntPtr sock,
1276                                                         byte[] buf, int offset,
1277                                                         int count,
1278                                                         SocketFlags flags);
1279
1280                 public int Send (byte[] buf, int offset, int size, SocketFlags flags)
1281                 {
1282                         if (buf == null)
1283                                 throw new ArgumentNullException ("buffer");
1284
1285                         if (offset < 0 || offset > buf.Length)
1286                                 throw new ArgumentOutOfRangeException ("offset");
1287
1288                         if (size < 0 || offset + size > buf.Length)
1289                                 throw new ArgumentOutOfRangeException ("size");
1290
1291                         if (size == 0)
1292                                 return 0;
1293
1294                         int ret;
1295
1296                         try {
1297                                 ret = Send_internal (socket, buf, offset, size, flags);
1298                         } catch (SocketException) {
1299                                 connected = false;
1300                                 throw;
1301                         }
1302                         connected = true;
1303
1304                         return ret;
1305                 }
1306
1307                 public int SendTo(byte[] buffer, EndPoint remote_end) {
1308                         return(SendTo(buffer, 0, buffer.Length,
1309                                       SocketFlags.None, remote_end));
1310                 }
1311
1312                 public int SendTo(byte[] buffer, SocketFlags flags,
1313                                   EndPoint remote_end) {
1314                         return(SendTo(buffer, 0, buffer.Length, flags,
1315                                       remote_end));
1316                 }
1317
1318                 public int SendTo(byte[] buffer, int size, SocketFlags flags,
1319                                   EndPoint remote_end) {
1320                         return(SendTo(buffer, 0, size, flags, remote_end));
1321                 }
1322
1323
1324                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1325                 private extern static int SendTo_internal(IntPtr sock,
1326                                                           byte[] buffer,
1327                                                           int offset,
1328                                                           int count,
1329                                                           SocketFlags flags,
1330                                                           SocketAddress sa);
1331
1332                 public int SendTo(byte[] buffer, int offset, int size,
1333                                   SocketFlags flags, EndPoint remote_end) {
1334                         if(buffer==null) {
1335                                 throw new ArgumentNullException("buffer is null");
1336                         }
1337                         if(remote_end==null) {
1338                                 throw new ArgumentNullException("remote endpoint is null");
1339                         }
1340                         if(offset<0 || offset>buffer.Length) {
1341                                 throw new ArgumentOutOfRangeException("offset exceeds the size of buffer");
1342                         }
1343                         if(size<0 || offset+size>buffer.Length) {
1344                                 throw new ArgumentOutOfRangeException("offset+size exceeds the size of buffer");
1345                         }
1346
1347                         SocketAddress sockaddr=remote_end.Serialize();
1348
1349                         int ret;
1350
1351                         try {
1352                                 ret=SendTo_internal(socket, buffer, offset,
1353                                                     size, flags, sockaddr);
1354                         }
1355                         catch(SocketException) {
1356                                 connected=false;
1357                                 throw;
1358                         }
1359                         connected=true;
1360
1361                         return(ret);
1362                 }
1363
1364                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1365                 private extern static void SetSocketOption_internal(IntPtr socket, SocketOptionLevel level, SocketOptionName name, object obj_val, byte[] byte_val, int int_val);
1366
1367                 public void SetSocketOption(SocketOptionLevel level,
1368                                             SocketOptionName name,
1369                                             byte[] opt_value) {
1370                         SetSocketOption_internal(socket, level, name, null,
1371                                                  opt_value, 0);
1372                 }
1373
1374                 public void SetSocketOption(SocketOptionLevel level,
1375                                             SocketOptionName name,
1376                                             int opt_value) {
1377                         SetSocketOption_internal(socket, level, name, null,
1378                                                  null, opt_value);
1379                 }
1380
1381                 public void SetSocketOption(SocketOptionLevel level,
1382                                             SocketOptionName name,
1383                                             object opt_value) {
1384                         if(opt_value==null) {
1385                                 throw new ArgumentNullException();
1386                         }
1387                         
1388                         /* Passing a bool as the third parameter to
1389                          * SetSocketOption causes this overload to be
1390                          * used when in fact we want to pass the value
1391                          * to the runtime as an int.
1392                          */
1393                         if(opt_value is System.Boolean) {
1394                                 bool bool_val=(bool)opt_value;
1395                                 
1396                                 /* Stupid casting rules :-( */
1397                                 if(bool_val==true) {
1398                                         SetSocketOption_internal(socket, level,
1399                                                                  name, null,
1400                                                                  null, 1);
1401                                 } else {
1402                                         SetSocketOption_internal(socket, level,
1403                                                                  name, null,
1404                                                                  null, 0);
1405                                 }
1406                         } else {
1407                                 SetSocketOption_internal(socket, level, name,
1408                                                          opt_value, null, 0);
1409                         }
1410                 }
1411
1412                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
1413                 private extern static void Shutdown_internal(IntPtr socket, SocketShutdown how);
1414                 
1415                 public void Shutdown(SocketShutdown how) {
1416                         Shutdown_internal(socket, how);
1417                 }
1418
1419                 public override int GetHashCode ()
1420                 { 
1421                         return (int) socket; 
1422                 }
1423
1424                 private bool disposed;
1425                 
1426                 protected virtual void Dispose(bool explicitDisposing) {
1427                         // Check to see if Dispose has already been called
1428                         if(!this.disposed) {
1429                                 // If this is a call to Dispose,
1430                                 // dispose all managed resources.
1431                                 if(explicitDisposing) {
1432                                         // Free up stuff here
1433                                 }
1434
1435                                 // Release unmanaged resources
1436                                 this.disposed=true;
1437                         
1438                                 connected=false;
1439                                 if (Interlocked.CompareExchange (ref pendingEnds, 0, 0) == 0) {
1440                                         closed = true;
1441                                         Close_internal(socket);
1442                                 } else {
1443                                         Interlocked.CompareExchange (ref closeDelayed, 1, 0);
1444                                 }
1445                         }
1446                 }
1447
1448                 void IDisposable.Dispose ()
1449                 {
1450                         Dispose (true);
1451                         GC.SuppressFinalize (this);
1452                 }
1453                 
1454                 ~Socket () {
1455                         Dispose(false);
1456                 }
1457         }
1458 }