[asp.net] Implemented CustomErrorsRedirectMode
[mono.git] / mcs / class / System / System.Net.Sockets / UdpClient.cs
1 //
2 // System.Net.Sockets.UdpClient.cs
3 //
4 // Author:
5 //    Gonzalo Paniagua Javier <gonzalo@ximian.com>
6 //    Sridhar Kulkarni (sridharkulkarni@gmail.com)
7 //
8 // Copyright (C) Ximian, Inc. http://www.ximian.com
9 //
10
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 using System;
33 using System.Net;
34
35 namespace System.Net.Sockets
36 {
37         public class UdpClient : IDisposable
38         {
39                 private bool disposed = false;
40                 private bool active = false;
41                 private Socket socket;
42                 private AddressFamily family = AddressFamily.InterNetwork;
43 #if NET_2_0
44                 private byte[] recvbuffer;
45 #endif
46         
47 #region Constructors
48                 public UdpClient () : this(AddressFamily.InterNetwork)
49                 {
50                 }
51
52 #if NET_1_1
53                 public UdpClient(AddressFamily family)
54                 {
55                         if(family != AddressFamily.InterNetwork && family != AddressFamily.InterNetworkV6)
56 #if NET_2_0
57                                 throw new ArgumentException ("Family must be InterNetwork or InterNetworkV6", "family");
58 #else
59                                 throw new ArgumentException ("family");
60 #endif
61
62                         this.family = family;
63                         InitSocket (null);
64                 }
65 #endif
66
67                 public UdpClient (int port)
68                 {
69                         if (port < IPEndPoint.MinPort || port > IPEndPoint.MaxPort)
70                                 throw new ArgumentOutOfRangeException ("port");
71
72                         this.family = AddressFamily.InterNetwork;
73
74                         IPEndPoint localEP = new IPEndPoint (IPAddress.Any, port);
75                         InitSocket (localEP);
76                 }
77
78                 public UdpClient (IPEndPoint localEP)
79                 {
80                         if (localEP == null)
81                                 throw new ArgumentNullException ("localEP");
82
83                         this.family = localEP.AddressFamily;
84
85                         InitSocket (localEP);
86                 }
87
88 #if NET_1_1
89                 public UdpClient (int port, AddressFamily family)
90                 {
91                         if (family != AddressFamily.InterNetwork && family != AddressFamily.InterNetworkV6)
92 #if NET_2_0
93                                 throw new ArgumentException ("Family must be InterNetwork or InterNetworkV6", "family");
94 #else
95                                 throw new ArgumentException ("family");
96 #endif
97
98                         if (port < IPEndPoint.MinPort ||
99                             port > IPEndPoint.MaxPort) {
100                                 throw new ArgumentOutOfRangeException ("port");
101                         }
102
103                         this.family = family;
104
105                         IPEndPoint localEP;
106
107                         if (family == AddressFamily.InterNetwork)
108                                 localEP = new IPEndPoint (IPAddress.Any, port);
109                         else
110                                 localEP = new IPEndPoint (IPAddress.IPv6Any, port);
111                         InitSocket (localEP);
112                 }
113 #endif
114
115                 public UdpClient (string hostname, int port)
116                 {
117                         if (hostname == null)
118                                 throw new ArgumentNullException ("hostname");
119
120                         if (port < IPEndPoint.MinPort || port > IPEndPoint.MaxPort)
121                                 throw new ArgumentOutOfRangeException ("port");
122
123                         InitSocket (null);
124                         Connect (hostname, port);
125                 }
126
127                 private void InitSocket (EndPoint localEP)
128                 {
129                         if(socket != null) {
130                                 socket.Close();
131                                 socket = null;
132                         }
133
134                         socket = new Socket (family, SocketType.Dgram, ProtocolType.Udp);
135
136                         if (localEP != null)
137                                 socket.Bind (localEP);
138                 }
139
140 #endregion // Constructors
141 #region Public methods
142 #region Close
143                 public void Close ()
144                 {
145                         ((IDisposable) this).Dispose ();        
146                 }
147 #endregion
148 #region Connect
149
150                 void DoConnect (IPEndPoint endPoint)
151                 {
152                         /* Catch EACCES and turn on SO_BROADCAST then,
153                          * as UDP sockets don't have it set by default
154                          */
155                         try {
156                                 socket.Connect (endPoint);
157                         } catch (SocketException ex) {
158                                 if (ex.ErrorCode == (int)SocketError.AccessDenied) {
159                                         socket.SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
160                                         
161                                         socket.Connect (endPoint);
162                                 } else {
163                                         throw;
164                                 }
165                         }
166                 }
167                 
168                 public void Connect (IPEndPoint endPoint)
169                 {
170                         CheckDisposed ();
171                         if (endPoint == null)
172                                 throw new ArgumentNullException ("endPoint");
173
174                         DoConnect (endPoint);
175                         active = true;
176                 }
177
178                 public void Connect (IPAddress addr, int port)
179                 {
180                         if (addr == null)
181                                 throw new ArgumentNullException ("addr");
182
183                         if (port < IPEndPoint.MinPort || port > IPEndPoint.MaxPort)
184                                 throw new ArgumentOutOfRangeException ("port");
185
186
187                         Connect (new IPEndPoint (addr, port));
188                 }
189
190                 public void Connect (string hostname, int port)
191                 {
192                         if (port < IPEndPoint.MinPort || port > IPEndPoint.MaxPort)
193                                 throw new ArgumentOutOfRangeException ("port");
194
195                         IPAddress[] addresses = Dns.GetHostAddresses (hostname);
196                         for(int i=0; i<addresses.Length; i++) {
197                                 try {
198                                         this.family = addresses[i].AddressFamily;
199                                         Connect (new IPEndPoint (addresses[i], port));
200                                         break;
201                                 } catch(Exception e) {
202                                         if(i == addresses.Length - 1){
203                                                 if(socket != null) {
204                                                         socket.Close();
205                                                         socket = null;
206                                                 }
207                                                 /// This is the last entry, re-throw the exception
208                                                 throw e;
209                                         }
210                                 }
211                         }
212                 }
213 #endregion
214                 #region Multicast methods
215                 public void DropMulticastGroup (IPAddress multicastAddr)
216                 {
217                         CheckDisposed ();
218                         if (multicastAddr == null)
219                                 throw new ArgumentNullException ("multicastAddr");
220
221                         if(family == AddressFamily.InterNetwork)
222                                 socket.SetSocketOption (SocketOptionLevel.IP, SocketOptionName.DropMembership,
223                                         new MulticastOption (multicastAddr));
224 #if NET_1_1
225                         else
226                                 socket.SetSocketOption (SocketOptionLevel.IPv6, SocketOptionName.DropMembership,
227                                         new IPv6MulticastOption (multicastAddr));
228 #endif
229                 }
230
231 #if NET_1_1
232                 public void DropMulticastGroup (IPAddress multicastAddr,
233                                                 int ifindex)
234                 {
235                         CheckDisposed ();
236
237                         /* LAMESPEC: exceptions haven't been specified
238                          * for this overload.
239                          */
240                         if (multicastAddr == null) {
241                                 throw new ArgumentNullException ("multicastAddr");
242                         }
243
244                         /* Does this overload only apply to IPv6?
245                          * Only the IPv6MulticastOption has an
246                          * ifindex-using constructor.  The MS docs
247                          * don't say.
248                          */
249                         if (family == AddressFamily.InterNetworkV6) {
250                                 socket.SetSocketOption (SocketOptionLevel.IPv6, SocketOptionName.DropMembership, new IPv6MulticastOption (multicastAddr, ifindex));
251                         }
252                 }
253 #endif
254                 
255                 public void JoinMulticastGroup (IPAddress multicastAddr)
256                 {
257                         CheckDisposed ();
258
259                         if (multicastAddr == null)
260 #if NET_2_0
261                                 throw new ArgumentNullException ("multicastAddr");
262 #else
263                                 throw new NullReferenceException ();
264 #endif
265
266                         if(family == AddressFamily.InterNetwork)
267                                 socket.SetSocketOption (SocketOptionLevel.IP, SocketOptionName.AddMembership,
268                                         new MulticastOption (multicastAddr));
269 #if NET_1_1
270                         else
271                                 socket.SetSocketOption (SocketOptionLevel.IPv6, SocketOptionName.AddMembership,
272                                         new IPv6MulticastOption (multicastAddr));
273 #endif
274                 }
275
276 #if NET_1_1
277                 public void JoinMulticastGroup (int ifindex,
278                                                 IPAddress multicastAddr)
279                 {
280                         CheckDisposed ();
281
282                         if (multicastAddr == null)
283                                 throw new ArgumentNullException ("multicastAddr");
284
285                         if (family == AddressFamily.InterNetworkV6)
286                                 socket.SetSocketOption (SocketOptionLevel.IPv6, SocketOptionName.AddMembership, new IPv6MulticastOption (multicastAddr, ifindex));
287                         else
288                                 throw new SocketException ((int) SocketError.OperationNotSupported);
289                 }
290 #endif
291
292                 public void JoinMulticastGroup (IPAddress multicastAddr, int timeToLive)
293                 {
294                         CheckDisposed ();
295                         if (multicastAddr == null)
296                                 throw new ArgumentNullException ("multicastAddr");
297                         if (timeToLive < 0 || timeToLive > 255)
298                                 throw new ArgumentOutOfRangeException ("timeToLive");
299
300                         JoinMulticastGroup (multicastAddr);
301                         if(family == AddressFamily.InterNetwork)
302                                 socket.SetSocketOption (SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive,
303                                         timeToLive);
304 #if NET_1_1
305                         else
306                                 socket.SetSocketOption (SocketOptionLevel.IPv6, SocketOptionName.MulticastTimeToLive,
307                                         timeToLive);
308 #endif
309                 }
310
311 #if NET_2_0
312                 public void JoinMulticastGroup (IPAddress multicastAddr,
313                                                 IPAddress localAddress)
314                 {
315                         CheckDisposed ();
316
317                         if (family == AddressFamily.InterNetwork)
318                                 socket.SetSocketOption (SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption (multicastAddr, localAddress));
319                         else
320                                 throw new SocketException ((int) SocketError.OperationNotSupported);
321                 }
322 #endif
323
324                 #endregion
325                 #region Data I/O
326                 public byte [] Receive (ref IPEndPoint remoteEP)
327                 {
328                         CheckDisposed ();
329
330                         byte [] recBuffer = new byte [65536]; // Max. size
331                         EndPoint endPoint = new IPEndPoint (IPAddress.Any, 0);
332                         int dataRead = socket.ReceiveFrom (recBuffer, ref endPoint);
333                         if (dataRead < recBuffer.Length)
334                                 recBuffer = CutArray (recBuffer, dataRead);
335
336                         remoteEP = (IPEndPoint) endPoint;
337                         return recBuffer;
338                 }
339
340                 int DoSend (byte[] dgram, int bytes, IPEndPoint endPoint)
341                 {
342                         /* Catch EACCES and turn on SO_BROADCAST then,
343                          * as UDP sockets don't have it set by default
344                          */
345                         try {
346                                 if (endPoint == null) {
347                                         return(socket.Send (dgram, 0, bytes,
348                                                             SocketFlags.None));
349                                 } else {
350                                         return(socket.SendTo (dgram, 0, bytes,
351                                                               SocketFlags.None,
352                                                               endPoint));
353                                 }
354                         } catch (SocketException ex) {
355                                 if (ex.ErrorCode == (int)SocketError.AccessDenied) {
356                                         socket.SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
357                                         if (endPoint == null) {
358                                                 return(socket.Send (dgram, 0, bytes, SocketFlags.None));
359                                         } else {
360                                                 return(socket.SendTo (dgram, 0, bytes, SocketFlags.None, endPoint));
361                                         }
362                                 } else {
363                                         throw;
364                                 }
365                         }
366                 }
367                 
368                 public int Send (byte [] dgram, int bytes)
369                 {
370                         CheckDisposed ();
371                         if (dgram == null)
372                                 throw new ArgumentNullException ("dgram");
373
374                         if (!active)
375                                 throw new InvalidOperationException ("Operation not allowed on " + 
376                                                                      "non-connected sockets.");
377
378                         return(DoSend (dgram, bytes, null));
379                 }
380
381                 public int Send (byte [] dgram, int bytes, IPEndPoint endPoint)
382                 {
383                         CheckDisposed ();
384                         if (dgram == null)
385                                 throw new ArgumentNullException ("dgram is null");
386                         
387                         if (active) {
388                                 if (endPoint != null)
389                                         throw new InvalidOperationException ("Cannot send packets to an " +
390                                                                              "arbitrary host while connected.");
391
392                                 return(DoSend (dgram, bytes, null));
393                         }
394
395                         return(DoSend (dgram, bytes, endPoint));
396                 }
397
398                 public int Send (byte [] dgram, int bytes, string hostname, int port)
399                 {
400                         return Send (dgram, bytes, 
401                                      new IPEndPoint (Dns.GetHostAddresses (hostname) [0], port));
402                 }
403
404                 private byte [] CutArray (byte [] orig, int length)
405                 {
406                         byte [] newArray = new byte [length];
407                         Buffer.BlockCopy (orig, 0, newArray, 0, length);
408
409                         return newArray;
410                 }
411 #endregion
412
413 #if NET_2_0
414                 IAsyncResult DoBeginSend (byte[] datagram, int bytes,
415                                           IPEndPoint endPoint,
416                                           AsyncCallback requestCallback,
417                                           object state)
418                 {
419                         /* Catch EACCES and turn on SO_BROADCAST then,
420                          * as UDP sockets don't have it set by default
421                          */
422                         try {
423                                 if (endPoint == null) {
424                                         return(socket.BeginSend (datagram, 0, bytes, SocketFlags.None, requestCallback, state));
425                                 } else {
426                                         return(socket.BeginSendTo (datagram, 0, bytes, SocketFlags.None, endPoint, requestCallback, state));
427                                 }
428                         } catch (SocketException ex) {
429                                 if (ex.ErrorCode == (int)SocketError.AccessDenied) {
430                                         socket.SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
431                                         if (endPoint == null) {
432                                                 return(socket.BeginSend (datagram, 0, bytes, SocketFlags.None, requestCallback, state));
433                                         } else {
434                                                 return(socket.BeginSendTo (datagram, 0, bytes, SocketFlags.None, endPoint, requestCallback, state));
435                                         }
436                                 } else {
437                                         throw;
438                                 }
439                         }
440                 }
441                 
442                 public IAsyncResult BeginSend (byte[] datagram, int bytes,
443                                                AsyncCallback requestCallback,
444                                                object state)
445                 {
446                         return(BeginSend (datagram, bytes, null,
447                                           requestCallback, state));
448                 }
449                 
450                 public IAsyncResult BeginSend (byte[] datagram, int bytes,
451                                                IPEndPoint endPoint,
452                                                AsyncCallback requestCallback,
453                                                object state)
454                 {
455                         CheckDisposed ();
456
457                         if (datagram == null) {
458                                 throw new ArgumentNullException ("datagram");
459                         }
460                         
461                         return(DoBeginSend (datagram, bytes, endPoint,
462                                             requestCallback, state));
463                 }
464                 
465                 public IAsyncResult BeginSend (byte[] datagram, int bytes,
466                                                string hostname, int port,
467                                                AsyncCallback requestCallback,
468                                                object state)
469                 {
470                         return(BeginSend (datagram, bytes, new IPEndPoint (Dns.GetHostAddresses (hostname) [0], port), requestCallback, state));
471                 }
472                 
473                 public int EndSend (IAsyncResult asyncResult)
474                 {
475                         CheckDisposed ();
476                         
477                         if (asyncResult == null) {
478                                 throw new ArgumentNullException ("asyncResult is a null reference");
479                         }
480                         
481                         return(socket.EndSend (asyncResult));
482                 }
483                 
484                 public IAsyncResult BeginReceive (AsyncCallback callback,
485                                                   object state)
486                 {
487                         CheckDisposed ();
488
489                         recvbuffer = new byte[8192];
490                         
491                         EndPoint ep;
492                         
493                         if (family == AddressFamily.InterNetwork) {
494                                 ep = new IPEndPoint (IPAddress.Any, 0);
495                         } else {
496                                 ep = new IPEndPoint (IPAddress.IPv6Any, 0);
497                         }
498                         
499                         return(socket.BeginReceiveFrom (recvbuffer, 0, 8192,
500                                                         SocketFlags.None,
501                                                         ref ep,
502                                                         callback, state));
503                 }
504                 
505                 public byte[] EndReceive (IAsyncResult asyncResult,
506                                           ref IPEndPoint remoteEP)
507                 {
508                         CheckDisposed ();
509                         
510                         if (asyncResult == null) {
511                                 throw new ArgumentNullException ("asyncResult is a null reference");
512                         }
513                         
514                         EndPoint ep;
515                         
516                         if (family == AddressFamily.InterNetwork) {
517                                 ep = new IPEndPoint (IPAddress.Any, 0);
518                         } else {
519                                 ep = new IPEndPoint (IPAddress.IPv6Any, 0);
520                         }
521                         
522                         int bytes = socket.EndReceiveFrom (asyncResult,
523                                                            ref ep);
524                         remoteEP = (IPEndPoint)ep;
525
526                         /* Need to copy into a new array here, because
527                          * otherwise the returned array length is not
528                          * 'bytes'
529                          */
530                         byte[] buf = new byte[bytes];
531                         Array.Copy (recvbuffer, buf, bytes);
532                         
533                         return(buf);
534                 }
535 #endif
536                                 
537 #region Properties
538                 protected bool Active {
539                         get { return active; }
540                         set { active = value; }
541                 }
542
543 #if NET_2_0
544                 public
545 #else
546                 protected
547 #endif
548                 Socket Client {
549                         get { return socket; }
550                         set { socket = value; }
551                 }
552
553 #if NET_2_0
554                 public int Available
555                 {
556                         get {
557                                 return(socket.Available);
558                         }
559                 }
560                 
561 #if TARGET_JVM
562                 [MonoNotSupported ("Not supported as Socket.DontFragment is not supported")]
563 #endif
564                 public bool DontFragment
565                 {
566                         get {
567                                 return(socket.DontFragment);
568                         }
569                         set {
570                                 socket.DontFragment = value;
571                         }
572                 }
573
574 #if TARGET_JVM
575                 [MonoNotSupported ("Not supported as Socket.EnableBroadcast is not supported")]
576 #endif
577                 public bool EnableBroadcast
578                 {
579                         get {
580                                 return(socket.EnableBroadcast);
581                         }
582                         set {
583                                 socket.EnableBroadcast = value;
584                         }
585                 }
586                 
587 #if TARGET_JVM
588                 [MonoNotSupported ("Not supported as Socket.ExclusiveAddressUse is not supported")]
589 #endif
590                 public bool ExclusiveAddressUse
591                 {
592                         get {
593                                 return(socket.ExclusiveAddressUse);
594                         }
595                         set {
596                                 socket.ExclusiveAddressUse = value;
597                         }
598                 }
599                 
600 #if TARGET_JVM
601                 [MonoNotSupported ("Not supported as Socket.MulticastLoopback is not supported")]
602 #endif
603                 public bool MulticastLoopback
604                 {
605                         get {
606                                 return(socket.MulticastLoopback);
607                         }
608                         set {
609                                 socket.MulticastLoopback = value;
610                         }
611                 }
612                 
613 #if TARGET_JVM
614                 [MonoNotSupported ("Not supported as Socket.Ttl is not supported")]
615 #endif
616                 public short Ttl
617                 {
618                         get {
619                                 return(socket.Ttl);
620                         }
621                         set {
622                                 socket.Ttl = value;
623                         }
624                 }
625 #endif
626
627 #endregion
628 #region Disposing
629                 void IDisposable.Dispose ()
630                 {
631                         Dispose (true);
632                         GC.SuppressFinalize (this);
633                 }
634
635 #if NET_2_0
636                 protected virtual
637 #endif
638                 void Dispose (bool disposing)
639                 {
640                         if (disposed)
641                                 return;
642                         disposed = true;
643
644                         if (disposing){
645                                 if (socket != null)
646                                         socket.Close ();
647
648                                 socket = null;
649                         }
650                 }
651                 
652                 ~UdpClient ()
653                 {
654                         Dispose (false);
655                 }
656                 
657                 private void CheckDisposed ()
658                 {
659                         if (disposed)
660                                 throw new ObjectDisposedException (GetType().FullName);
661                 }               
662 #endregion
663 #endregion
664         }
665 }
666