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