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