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