Merge pull request #1304 from slluis/mac-proxy-autoconfig
[mono.git] / mcs / class / System / System.Net.Sockets / TcpClient.cs
1 // TcpClient.cs
2 //
3 // Author:
4 //      Phillip Pearson (pp@myelin.co.nz)
5 //      Gonzalo Paniagua Javier (gonzalo@novell.com)
6 //      Sridhar Kulkarni (sridharkulkarni@gmail.com)
7 //      Marek Safar (marek.safar@gmail.com)
8 //
9 // Copyright (C) 2001, Phillip Pearson http://www.myelin.co.nz
10 // Copyright (c) 2006 Novell, Inc. (http://www.novell.com)
11 // Copyright 2011 Xamarin Inc.
12 //
13
14 //
15 // Permission is hereby granted, free of charge, to any person obtaining
16 // a copy of this software and associated documentation files (the
17 // "Software"), to deal in the Software without restriction, including
18 // without limitation the rights to use, copy, modify, merge, publish,
19 // distribute, sublicense, and/or sell copies of the Software, and to
20 // permit persons to whom the Software is furnished to do so, subject to
21 // the following conditions:
22 // 
23 // The above copyright notice and this permission notice shall be
24 // included in all copies or substantial portions of the Software.
25 // 
26 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
27 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
28 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
29 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
30 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
31 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
32 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
33 //
34
35 using System;
36 using System.Net;
37 #if NET_4_5
38 using System.Threading.Tasks;
39 #endif
40
41 namespace System.Net.Sockets
42 {
43         public class TcpClient : IDisposable {
44                 enum Properties : uint {
45                         LingerState = 1,
46                         NoDelay = 2,
47                         ReceiveBufferSize = 4,
48                         ReceiveTimeout = 8,
49                         SendBufferSize = 16,
50                         SendTimeout = 32
51                 }
52
53                 // private data
54                 NetworkStream stream;
55                 bool active;
56                 Socket client;
57                 bool disposed;
58                 Properties values;
59                 int recv_timeout, send_timeout;
60                 int recv_buffer_size, send_buffer_size;
61                 LingerOption linger_state;
62                 bool no_delay;
63                 
64                 private void Init (AddressFamily family)
65                 {
66                         active = false;
67
68                         if(client != null) {
69                                 client.Close();
70                                 client = null;
71                         }
72
73                         client = new Socket(family, SocketType.Stream, ProtocolType.Tcp);
74                 }
75
76                 public TcpClient ()
77                 {
78                         Init(AddressFamily.InterNetwork);
79                         client.Bind(new IPEndPoint(IPAddress.Any, 0));
80                 }
81
82                 internal TcpClient (Socket s)
83                 {
84                         client = s;
85                 }
86         
87                 public TcpClient (AddressFamily family)
88                 {
89                         if (family != AddressFamily.InterNetwork &&
90                             family != AddressFamily.InterNetworkV6) {
91                                 throw new ArgumentException ("Family must be InterNetwork or InterNetworkV6", "family");
92                         }
93                         
94                         Init (family);
95                         IPAddress any = IPAddress.Any;
96                         if (family == AddressFamily.InterNetworkV6)
97                                 any = IPAddress.IPv6Any;
98                         client.Bind (new IPEndPoint (any, 0));
99                 }
100
101                 public TcpClient (IPEndPoint localEP)
102                 {
103                         Init (localEP.AddressFamily);
104                         client.Bind (localEP);
105                 }
106                 
107                 public TcpClient (string hostname, int port)
108                 {
109                         Connect(hostname, port);
110                 }
111                                 
112                 protected bool Active {
113                         get { return active; }
114                         set { active = value; }
115                 }
116                 
117                 public Socket Client {
118                         get { return client; }
119                         set {
120                                 client = value;
121                                 stream = null;
122                         }
123                 }
124
125                 public int Available {
126                         get { return client.Available; }
127                 }
128
129                 public bool Connected {
130                         get { return client.Connected; }
131                 }
132
133                 public bool ExclusiveAddressUse {
134                         get {
135                                 return(client.ExclusiveAddressUse);
136                         }
137                         set {
138                                 client.ExclusiveAddressUse = value;
139                         }
140                 }
141
142                 public LingerOption LingerState {
143                         get {
144                                 if ((values & Properties.LingerState) != 0)
145                                         return linger_state;
146
147                                 return (LingerOption) client.GetSocketOption (SocketOptionLevel.Socket,
148                                                                         SocketOptionName.Linger);
149                         }
150                         set {
151                                 if (!client.Connected) {
152                                         linger_state = value;
153                                         values |= Properties.LingerState;
154                                         return;
155                                 }
156                                 client.SetSocketOption(
157                                         SocketOptionLevel.Socket,
158                                         SocketOptionName.Linger, value);
159                         }
160                 }
161                                 
162                 public bool NoDelay {
163                         get {
164                                 if ((values & Properties.NoDelay) != 0)
165                                         return no_delay;
166
167                                 return (bool)client.GetSocketOption(
168                                         SocketOptionLevel.Tcp,
169                                         SocketOptionName.NoDelay);
170                         }
171                         set {
172                                 if (!client.Connected) {
173                                         no_delay = value;
174                                         values |= Properties.NoDelay;
175                                         return;
176                                 }
177                                 client.SetSocketOption(
178                                         SocketOptionLevel.Tcp,
179                                         SocketOptionName.NoDelay, value ? 1 : 0);
180                         }
181                 }
182                                 
183                 public int ReceiveBufferSize {
184                         get {
185                                 if ((values & Properties.ReceiveBufferSize) != 0)
186                                         return recv_buffer_size;
187
188                                 return (int)client.GetSocketOption(
189                                         SocketOptionLevel.Socket,
190                                         SocketOptionName.ReceiveBuffer);
191                         }
192                         set {
193                                 if (!client.Connected) {
194                                         recv_buffer_size = value;
195                                         values |= Properties.ReceiveBufferSize;
196                                         return;
197                                 }
198                                 client.SetSocketOption(
199                                         SocketOptionLevel.Socket,
200                                         SocketOptionName.ReceiveBuffer, value);
201                         }
202                 }
203                         
204                 public int ReceiveTimeout {
205                         get {
206                                 if ((values & Properties.ReceiveTimeout) != 0)
207                                         return recv_timeout;
208
209                                 return (int)client.GetSocketOption(
210                                         SocketOptionLevel.Socket,
211                                         SocketOptionName.ReceiveTimeout);
212                         }
213                         set {
214                                 if (!client.Connected) {
215                                         recv_timeout = value;
216                                         values |= Properties.ReceiveTimeout;
217                                         return;
218                                 }
219                                 client.SetSocketOption(
220                                         SocketOptionLevel.Socket,
221                                         SocketOptionName.ReceiveTimeout, value);
222                         }
223                 }
224                 
225                 public int SendBufferSize {
226                         get {
227                                 if ((values & Properties.SendBufferSize) != 0)
228                                         return send_buffer_size;
229
230                                 return (int)client.GetSocketOption(
231                                         SocketOptionLevel.Socket,
232                                         SocketOptionName.SendBuffer);
233                         }
234                         set {
235                                 if (!client.Connected) {
236                                         send_buffer_size = value;
237                                         values |= Properties.SendBufferSize;
238                                         return;
239                                 }
240                                 client.SetSocketOption(
241                                         SocketOptionLevel.Socket,
242                                         SocketOptionName.SendBuffer, value);
243                         }
244                 }
245                 
246                 public int SendTimeout {
247                         get {
248                                 if ((values & Properties.SendTimeout) != 0)
249                                         return send_timeout;
250
251                                 return (int)client.GetSocketOption(
252                                         SocketOptionLevel.Socket,
253                                         SocketOptionName.SendTimeout);
254                         }
255                         set {
256                                 if (!client.Connected) {
257                                         send_timeout = value;
258                                         values |= Properties.SendTimeout;
259                                         return;
260                                 }
261                                 client.SetSocketOption(
262                                         SocketOptionLevel.Socket,
263                                         SocketOptionName.SendTimeout, value);
264                         }
265                 }
266                 
267                 
268                 // methods
269                 
270                 public void Close ()
271                 {
272                         ((IDisposable) this).Dispose ();
273                 }
274
275                 public void Connect (IPEndPoint remoteEP)
276                 {
277                         try {
278                                 client.Connect (remoteEP);
279                                 active = true;
280                         } finally {
281                                 CheckDisposed ();
282                         }
283                 }
284                 
285                 public void Connect (IPAddress address, int port)
286                 {
287                         Connect(new IPEndPoint(address, port));
288                 }
289
290                 void SetOptions ()
291                 {
292                         Properties props = values;
293                         values = 0;
294
295                         if ((props & Properties.LingerState) != 0)
296                                 LingerState = linger_state;
297                         if ((props & Properties.NoDelay) != 0)
298                                 NoDelay = no_delay;
299                         if ((props & Properties.ReceiveBufferSize) != 0)
300                                 ReceiveBufferSize = recv_buffer_size;
301                         if ((props & Properties.ReceiveTimeout) != 0)
302                                 ReceiveTimeout = recv_timeout;
303                         if ((props & Properties.SendBufferSize) != 0)
304                                 SendBufferSize = send_buffer_size;
305                         if ((props & Properties.SendTimeout) != 0)
306                                 SendTimeout = send_timeout;
307                 }
308
309                 public void Connect (string hostname, int port)
310                 {
311                         IPAddress [] addresses = Dns.GetHostAddresses (hostname);
312                         Connect (addresses, port);
313                 }
314
315                 public void Connect (IPAddress[] ipAddresses, int port)
316                 {
317                         CheckDisposed ();
318                         
319                         if (ipAddresses == null) {
320                                 throw new ArgumentNullException ("ipAddresses");
321                         }
322                         
323                         for(int i = 0; i < ipAddresses.Length; i++) {
324                                 try {
325                                         IPAddress address = ipAddresses[i];
326
327                                         if (address.Equals (IPAddress.Any) ||
328                                             address.Equals (IPAddress.IPv6Any)) {
329                                                 throw new SocketException ((int)SocketError.AddressNotAvailable);
330                                         }
331
332                                         Init (address.AddressFamily);
333                                         
334                                         if (address.AddressFamily == AddressFamily.InterNetwork) {
335                                                 client.Bind (new IPEndPoint (IPAddress.Any, 0));
336                                         } else if (address.AddressFamily == AddressFamily.InterNetworkV6) {
337                                                 client.Bind (new IPEndPoint (IPAddress.IPv6Any, 0));
338                                         } else {
339                                                 throw new NotSupportedException ("This method is only valid for sockets in the InterNetwork and InterNetworkV6 families");
340                                         }
341
342                                         Connect (new IPEndPoint (address, port));
343                                         
344                                         if (values != 0) {
345                                                 SetOptions ();
346                                         }
347                                         
348                                         break;
349                                 } catch (Exception e) {
350                                         /* Reinitialise the socket so
351                                          * other properties still work
352                                          * (see no-arg constructor)
353                                          */
354                                         Init (AddressFamily.InterNetwork);
355
356                                         /* This is the last known
357                                          * address, so re-throw the
358                                          * exception
359                                          */
360                                         if (i == ipAddresses.Length - 1) {
361                                                 throw e;
362                                         }
363                                 }
364                         }
365                 }
366                 
367                 public void EndConnect (IAsyncResult asyncResult)
368                 {
369                         client.EndConnect (asyncResult);
370                 }
371                 
372                 public IAsyncResult BeginConnect (IPAddress address, int port, AsyncCallback requestCallback, object state)
373                 {
374                         return client.BeginConnect (address, port, requestCallback, state);
375                 }
376                 
377                 public IAsyncResult BeginConnect (IPAddress[] addresses, int port, AsyncCallback requestCallback, object state)
378                 {
379                         return client.BeginConnect (addresses, port, requestCallback, state);
380                 }
381                 
382                 public IAsyncResult BeginConnect (string host, int port, AsyncCallback requestCallback, object state)
383                 {
384                         return client.BeginConnect (host, port, requestCallback, state);
385                 }
386                 
387                 void IDisposable.Dispose ()
388                 {
389                         Dispose (true);
390                         GC.SuppressFinalize (this);
391                 }
392
393                 protected virtual void Dispose (bool disposing)
394                 {
395                         if (disposed)
396                                 return;
397                         disposed = true;
398
399                         if (disposing) {
400                                 // release managed resources
401                                 NetworkStream s = stream;
402                                 stream = null;
403                                 if (s != null) {
404                                         // This closes the socket as well, as the NetworkStream
405                                         // owns the socket.
406                                         s.Close();
407                                         active = false;
408                                         s = null;
409                                 } else if (client != null){
410                                         client.Close ();
411                                         client = null;
412                                 }
413                         }
414                 }
415                 
416                 ~TcpClient ()
417                 {
418                         Dispose (false);
419                 }
420                 
421                 public NetworkStream GetStream()
422                 {
423                         try {
424                                 if (stream == null)
425                                         stream = new NetworkStream (client, true);
426                                 return stream;
427                         }
428                         finally { CheckDisposed (); }
429                 }
430
431 #if NET_4_5
432                 public Task ConnectAsync (IPAddress address, int port)
433                 {
434                         return Task.Factory.FromAsync (BeginConnect, EndConnect, address, port, null);
435                 }
436
437                 public Task ConnectAsync (IPAddress[] addresses, int port)
438                 {
439                         return Task.Factory.FromAsync (BeginConnect, EndConnect, addresses, port, null);
440                 }
441
442                 public Task ConnectAsync (string host, int port)
443                 {
444                         return Task.Factory.FromAsync (BeginConnect, EndConnect, host, port, null);
445                 }
446 #endif
447                 private void CheckDisposed ()
448                 {
449                         if (disposed)
450                                 throw new ObjectDisposedException (GetType().FullName);
451                 }
452         }
453 }
454