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