Merge pull request #588 from Daniel15/bug-10872
[mono.git] / mcs / class / System / System.Net / Dns.cs
1 // System.Net.Dns.cs
2 //
3 // Authors:
4 //      Mads Pultz (mpultz@diku.dk)
5 //      Lawrence Pit (loz@cable.a2000.nl)
6
7 // Author: Mads Pultz (mpultz@diku.dk)
8 //         Lawrence Pit (loz@cable.a2000.nl)
9 //         Marek Safar (marek.safar@gmail.com)
10 //         Gonzalo Paniagua Javier (gonzalo.mono@gmail.com)
11 //
12 // (C) Mads Pultz, 2001
13 // Copyright (c) 2011 Novell, Inc.
14 // Copyright (c) 2011 Xamarin, Inc.
15
16 //
17 // Permission is hereby granted, free of charge, to any person obtaining
18 // a copy of this software and associated documentation files (the
19 // "Software"), to deal in the Software without restriction, including
20 // without limitation the rights to use, copy, modify, merge, publish,
21 // distribute, sublicense, and/or sell copies of the Software, and to
22 // permit persons to whom the Software is furnished to do so, subject to
23 // the following conditions:
24 // 
25 // The above copyright notice and this permission notice shall be
26 // included in all copies or substantial portions of the Software.
27 // 
28 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
29 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
30 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
31 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
32 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
33 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
34 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
35 //
36
37 using System;
38 using System.Net.Sockets;
39 using System.Text;
40 using System.Collections;
41 using System.Threading;
42 using System.Runtime.CompilerServices;
43 using System.Runtime.Remoting.Messaging;
44 #if NET_4_5
45 using System.Threading.Tasks;
46 #endif
47
48 #if !MOBILE
49 using Mono.Net.Dns;
50 #endif
51
52 namespace System.Net {
53         public static class Dns {
54 #if !MOBILE
55                 static bool use_mono_dns;
56                 static SimpleResolver resolver;
57 #endif
58
59                 static Dns ()
60                 {
61                         System.Net.Sockets.Socket.CheckProtocolSupport();
62
63 #if !MOBILE
64                         if (Environment.GetEnvironmentVariable ("MONO_DNS") != null) {
65                                 resolver = new SimpleResolver ();
66                                 use_mono_dns = true;
67                         }
68 #endif
69                 }
70
71 #if !MOBILE
72                 internal static bool UseMonoDns {
73                         get { return use_mono_dns; }
74                 }
75 #endif
76
77                 private delegate IPHostEntry GetHostByNameCallback (string hostName);
78                 private delegate IPHostEntry ResolveCallback (string hostName);
79                 private delegate IPHostEntry GetHostEntryNameCallback (string hostName);
80                 private delegate IPHostEntry GetHostEntryIPCallback (IPAddress hostAddress);
81                 private delegate IPAddress [] GetHostAddressesCallback (string hostName);
82
83 #if !MOBILE
84                 static void OnCompleted (object sender, SimpleResolverEventArgs e)
85                 {
86                         DnsAsyncResult ares = (DnsAsyncResult) e.UserToken;
87                         IPHostEntry entry = e.HostEntry;
88                         if (entry == null || e.ResolverError != 0) {
89                                 ares.SetCompleted (false, new Exception ("Error: " + e.ResolverError));
90                                 return;
91                         }
92                         ares.SetCompleted (false, entry);
93                 }
94
95                 static IAsyncResult BeginAsyncCallAddresses (string host, AsyncCallback callback, object state)
96                 {
97                         SimpleResolverEventArgs e = new SimpleResolverEventArgs ();
98                         e.Completed += OnCompleted;
99                         e.HostName = host;
100                         DnsAsyncResult ares = new DnsAsyncResult (callback, state);
101                         e.UserToken = ares;
102                         if (resolver.GetHostAddressesAsync (e) == false)
103                                 ares.SetCompleted (true, e.HostEntry); // Completed synchronously
104                         return ares;
105                 }
106
107                 static IAsyncResult BeginAsyncCall (string host, AsyncCallback callback, object state)
108                 {
109                         SimpleResolverEventArgs e = new SimpleResolverEventArgs ();
110                         e.Completed += OnCompleted;
111                         e.HostName = host;
112                         DnsAsyncResult ares = new DnsAsyncResult (callback, state);
113                         e.UserToken = ares;
114                         if (resolver.GetHostEntryAsync (e) == false)
115                                 ares.SetCompleted (true, e.HostEntry); // Completed synchronously
116                         return ares;
117                 }
118
119                 static IPHostEntry EndAsyncCall (DnsAsyncResult ares)
120                 {
121                         if (ares == null)
122                                 throw new ArgumentException ("Invalid asyncResult");
123                         if (!ares.IsCompleted)
124                                 ares.AsyncWaitHandle.WaitOne ();
125                         if (ares.Exception != null)
126                                 throw ares.Exception;
127                         IPHostEntry entry = ares.HostEntry;
128                         if (entry == null || entry.AddressList == null || entry.AddressList.Length == 0)
129                                 throw new SocketException(11001);
130                         return entry;
131                 }
132 #endif
133
134                 [Obsolete ("Use BeginGetHostEntry instead")]
135                 public static IAsyncResult BeginGetHostByName (string hostName, AsyncCallback requestCallback, object stateObject)
136                 {
137                         if (hostName == null)
138                                 throw new ArgumentNullException ("hostName");
139
140 #if !MOBILE
141                         if (use_mono_dns)
142                                 return BeginAsyncCall (hostName, requestCallback, stateObject);
143 #endif
144
145                         GetHostByNameCallback c = new GetHostByNameCallback (GetHostByName);
146                         return c.BeginInvoke (hostName, requestCallback, stateObject);
147                 }
148
149                 [Obsolete ("Use BeginGetHostEntry instead")]
150                 public static IAsyncResult BeginResolve (string hostName, AsyncCallback requestCallback, object stateObject)
151                 {
152                         if (hostName == null)
153                                 throw new ArgumentNullException ("hostName");
154
155 #if !MOBILE
156                         if (use_mono_dns)
157                                 return BeginAsyncCall (hostName, requestCallback, stateObject);
158 #endif
159
160                         ResolveCallback c = new ResolveCallback (Resolve);
161                         return c.BeginInvoke (hostName, requestCallback, stateObject);
162                 }
163
164                 public static IAsyncResult BeginGetHostAddresses (string hostNameOrAddress, AsyncCallback requestCallback, object state)
165                 {
166                         if (hostNameOrAddress == null)
167                                 throw new ArgumentNullException ("hostName");
168                         if (hostNameOrAddress == "0.0.0.0" || hostNameOrAddress == "::0")
169                                 throw new ArgumentException ("Addresses 0.0.0.0 (IPv4) " +
170                                         "and ::0 (IPv6) are unspecified addresses. You " +
171                                         "cannot use them as target address.",
172                                         "hostNameOrAddress");
173
174 #if !MOBILE
175                         if (use_mono_dns)
176                                 return BeginAsyncCallAddresses (hostNameOrAddress, requestCallback, state);
177 #endif
178
179                         GetHostAddressesCallback c = new GetHostAddressesCallback (GetHostAddresses);
180                         return c.BeginInvoke (hostNameOrAddress, requestCallback, state);
181                 }
182
183                 public static IAsyncResult BeginGetHostEntry (string hostNameOrAddress, AsyncCallback requestCallback, object stateObject)
184                 {
185                         if (hostNameOrAddress == null)
186                                 throw new ArgumentNullException ("hostName");
187                         if (hostNameOrAddress == "0.0.0.0" || hostNameOrAddress == "::0")
188                                 throw new ArgumentException ("Addresses 0.0.0.0 (IPv4) " +
189                                         "and ::0 (IPv6) are unspecified addresses. You " +
190                                         "cannot use them as target address.",
191                                         "hostNameOrAddress");
192
193 #if !MOBILE
194                         if (use_mono_dns)
195                                 return BeginAsyncCall (hostNameOrAddress, requestCallback, stateObject);
196 #endif
197
198                         GetHostEntryNameCallback c = new GetHostEntryNameCallback (GetHostEntry);
199                         return c.BeginInvoke (hostNameOrAddress, requestCallback, stateObject);
200                 }
201
202                 public static IAsyncResult BeginGetHostEntry (IPAddress address, AsyncCallback requestCallback, object stateObject)
203                 {
204                         if (address == null)
205                                 throw new ArgumentNullException ("address");
206
207 #if !MOBILE
208                         if (use_mono_dns)
209                                 return BeginAsyncCall (address.ToString (), requestCallback, stateObject);
210 #endif
211
212                         GetHostEntryIPCallback c = new GetHostEntryIPCallback (GetHostEntry);
213                         return c.BeginInvoke (address, requestCallback, stateObject);
214                 }
215
216                 [Obsolete ("Use EndGetHostEntry instead")]
217                 public static IPHostEntry EndGetHostByName (IAsyncResult asyncResult) 
218                 {
219                         if (asyncResult == null)
220                                 throw new ArgumentNullException ("asyncResult");
221
222 #if !MOBILE
223                         if (use_mono_dns)
224                                 return EndAsyncCall (asyncResult as DnsAsyncResult);
225 #endif
226
227                         AsyncResult async = (AsyncResult) asyncResult;
228                         GetHostByNameCallback cb = (GetHostByNameCallback) async.AsyncDelegate;
229                         return cb.EndInvoke(asyncResult);
230                 }
231
232                 [Obsolete ("Use EndGetHostEntry instead")]
233                 public static IPHostEntry EndResolve (IAsyncResult asyncResult) 
234                 {
235                         if (asyncResult == null)
236                                 throw new ArgumentNullException ("asyncResult");
237
238 #if !MOBILE
239                         if (use_mono_dns)
240                                 return EndAsyncCall (asyncResult as DnsAsyncResult);
241 #endif
242
243                         AsyncResult async = (AsyncResult) asyncResult;
244                         ResolveCallback cb = (ResolveCallback) async.AsyncDelegate;
245                         return cb.EndInvoke(asyncResult);
246                 }
247
248                 public static IPAddress [] EndGetHostAddresses (IAsyncResult asyncResult) 
249                 {
250                         if (asyncResult == null)
251                                 throw new ArgumentNullException ("asyncResult");
252
253 #if !MOBILE
254                         if (use_mono_dns) {
255                                 IPHostEntry entry = EndAsyncCall (asyncResult as DnsAsyncResult);
256                                 if (entry == null)
257                                         return null;
258                                 return entry.AddressList;
259                         }
260 #endif
261
262                         AsyncResult async = (AsyncResult) asyncResult;
263                         GetHostAddressesCallback cb = (GetHostAddressesCallback) async.AsyncDelegate;
264                         return cb.EndInvoke(asyncResult);
265                 }
266
267                 public static IPHostEntry EndGetHostEntry (IAsyncResult asyncResult) 
268                 {
269                         if (asyncResult == null)
270                                 throw new ArgumentNullException ("asyncResult");
271
272 #if !MOBILE
273                         if (use_mono_dns)
274                                 return EndAsyncCall (asyncResult as DnsAsyncResult);
275 #endif
276
277                         AsyncResult async = (AsyncResult) asyncResult;
278                         if (async.AsyncDelegate is GetHostEntryIPCallback)
279                                 return ((GetHostEntryIPCallback) async.AsyncDelegate).EndInvoke (asyncResult);
280                         GetHostEntryNameCallback cb = (GetHostEntryNameCallback) async.AsyncDelegate;
281                         return cb.EndInvoke(asyncResult);
282                 }
283
284
285 #if !TARGET_JVM
286                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
287                 private extern static bool GetHostByName_internal(string host, out string h_name, out string[] h_aliases, out string[] h_addr_list);
288
289                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
290                 private extern static bool GetHostByAddr_internal(string addr, out string h_name, out string[] h_aliases, out string[] h_addr_list);
291
292                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
293                 private extern static bool GetHostName_internal(out string h_name);
294 #endif  
295
296                 private static IPHostEntry hostent_to_IPHostEntry(string h_name, string[] h_aliases, string[] h_addrlist) 
297                 {
298                         IPHostEntry he = new IPHostEntry();
299                         ArrayList addrlist = new ArrayList();
300
301                         he.HostName = h_name;
302                         he.Aliases = h_aliases;
303                         for(int i=0; i<h_addrlist.Length; i++) {
304                                 try {
305                                         IPAddress newAddress = IPAddress.Parse(h_addrlist[i]);
306
307                                         if( (Socket.SupportsIPv6 && newAddress.AddressFamily == AddressFamily.InterNetworkV6) ||
308                                             (Socket.SupportsIPv4 && newAddress.AddressFamily == AddressFamily.InterNetwork) )
309                                                 addrlist.Add(newAddress);
310                                 } catch (ArgumentNullException) {
311                                         /* Ignore this, as the
312                                          * internal call might have
313                                          * left some blank entries at
314                                          * the end of the array
315                                          */
316                                 }
317                         }
318
319                         if(addrlist.Count == 0)
320                                 throw new SocketException(11001);
321
322                         he.AddressList = addrlist.ToArray(typeof(IPAddress)) as IPAddress[];
323                         return he;
324                 }
325
326                 [Obsolete ("Use GetHostEntry instead")]
327                 public static IPHostEntry GetHostByAddress(IPAddress address)
328                 {
329                         if (address == null)
330                                 throw new ArgumentNullException ("address");
331
332                         return GetHostByAddressFromString (address.ToString (), false);
333                 }
334
335                 [Obsolete ("Use GetHostEntry instead")]
336                 public static IPHostEntry GetHostByAddress(string address)
337                 {
338                         if (address == null)
339                                 throw new ArgumentNullException ("address");
340
341                         return GetHostByAddressFromString (address, true);
342                 }
343
344                 static IPHostEntry GetHostByAddressFromString (string address, bool parse)
345                 {
346                         // Undocumented MS behavior: when called with IF_ANY,
347                         // this should return the local host
348                         if (address.Equals ("0.0.0.0")) {
349                                 address = "127.0.0.1";
350                                 parse = false;
351                         }
352
353                         // Must check the IP format, might send an exception if invalid string.
354                         if (parse)
355                                 IPAddress.Parse (address);
356
357                         string h_name;
358                         string[] h_aliases, h_addrlist;
359 #if TARGET_JVM
360                         h_name = null;
361                         h_aliases = null;
362                         h_addrlist = null;
363                         try {
364                                 java.net.InetAddress[] iaArr = 
365                                         java.net.InetAddress.getAllByName(address);
366                                 if (iaArr != null && iaArr.Length > 0)
367                                     h_name = iaArr[0].getHostName();
368                                 if (iaArr != null && iaArr.Length > 0)
369                                 {
370                                     h_addrlist = new String[iaArr.Length];
371                                     for (int i = 0; i < h_addrlist.Length; i++)
372                                         h_addrlist[i] = iaArr[i].getHostAddress();
373                                 }
374                         } catch (java.net.UnknownHostException jUHE) {
375                                 throw new SocketException((int)SocketError.HostNotFound, jUHE.Message);
376                         }
377 #else
378                         bool ret = GetHostByAddr_internal(address, out h_name, out h_aliases, out h_addrlist);
379                         if (!ret)
380                                 throw new SocketException(11001);
381 #endif
382                         return (hostent_to_IPHostEntry (h_name, h_aliases, h_addrlist));
383                         
384                 }
385
386                 public static IPHostEntry GetHostEntry (string hostNameOrAddress)
387                 {
388                         if (hostNameOrAddress == null)
389                                 throw new ArgumentNullException ("hostNameOrAddress");
390                         if (hostNameOrAddress == "0.0.0.0" || hostNameOrAddress == "::0")
391                                 throw new ArgumentException ("Addresses 0.0.0.0 (IPv4) " +
392                                         "and ::0 (IPv6) are unspecified addresses. You " +
393                                         "cannot use them as target address.",
394                                         "hostNameOrAddress");
395
396                         IPAddress addr;
397                         if (hostNameOrAddress.Length > 0 && IPAddress.TryParse (hostNameOrAddress, out addr))
398                                 return GetHostEntry (addr);
399
400                         return GetHostByName (hostNameOrAddress);
401                 }
402
403                 public static IPHostEntry GetHostEntry (IPAddress address)
404                 {
405                         if (address == null)
406                                 throw new ArgumentNullException ("address");
407
408                         return GetHostByAddressFromString (address.ToString (), false);
409                 }
410
411                 public static IPAddress [] GetHostAddresses (string hostNameOrAddress)
412                 {
413                         if (hostNameOrAddress == null)
414                                 throw new ArgumentNullException ("hostNameOrAddress");
415
416                         if (hostNameOrAddress == "0.0.0.0" || hostNameOrAddress == "::0")
417                                 throw new ArgumentException ("Addresses 0.0.0.0 (IPv4) " +
418                                         "and ::0 (IPv6) are unspecified addresses. You " +
419                                         "cannot use them as target address.",
420                                         "hostNameOrAddress");
421
422                         IPAddress addr;
423                         if (hostNameOrAddress.Length > 0 && IPAddress.TryParse (hostNameOrAddress, out addr))
424                                 return new IPAddress[1] { addr };
425
426                         return GetHostEntry (hostNameOrAddress).AddressList;
427                 }
428
429                 [Obsolete ("Use GetHostEntry instead")]
430                 public static IPHostEntry GetHostByName (string hostName)
431                 {
432                         if (hostName == null)
433                                 throw new ArgumentNullException ("hostName");
434 #if TARGET_JVM
435                         if (hostName.Length == 0)
436                                 hostName = "localhost";
437                         try {
438                                 java.net.InetAddress[] iaArr = java.net.InetAddress.getAllByName(hostName);
439                                 IPHostEntry host = new IPHostEntry();
440                                 if (iaArr != null && iaArr.Length > 0)
441                                 {
442                                         host.HostName = iaArr[0].getHostName();
443                                         IPAddress[] ipArr = new IPAddress[iaArr.Length];
444                                         for (int i = 0; i < iaArr.Length; i++)
445                                                 ipArr[i] = IPAddress.Parse(iaArr[i].getHostAddress());
446
447                                         host.AddressList = ipArr;
448                                 }
449                                 return host;
450                         } catch (java.net.UnknownHostException jUHE) {
451                                 throw new SocketException((int)SocketError.HostNotFound, jUHE.Message);
452                         }
453 #else
454                         string h_name;
455                         string[] h_aliases, h_addrlist;
456
457                         bool ret = GetHostByName_internal(hostName, out h_name, out h_aliases, out h_addrlist);
458                         if (ret == false)
459                                 throw new SocketException(11001);
460
461                         return(hostent_to_IPHostEntry(h_name, h_aliases, h_addrlist));
462 #endif
463                 }
464
465                 public static string GetHostName ()
466                 {
467 #if TARGET_JVM
468                         return java.net.InetAddress.getLocalHost ().getHostName ();
469 #else
470                         string hostName;
471
472                         bool ret = GetHostName_internal(out hostName);
473
474                         if (ret == false)
475                                 throw new SocketException(11001);
476
477                         return hostName;
478 #endif
479                 }
480
481                 [Obsolete ("Use GetHostEntry instead")]
482                 public static IPHostEntry Resolve(string hostName) 
483                 {
484                         if (hostName == null)
485                                 throw new ArgumentNullException ("hostName");
486
487                         IPHostEntry ret = null;
488
489                         try {
490                                 ret =  GetHostByAddress(hostName);
491                         }
492                         catch{}
493
494                         if(ret == null)
495                                 ret =  GetHostByName(hostName);
496
497                         return ret;
498                 }
499
500 #if NET_4_5
501                 public static Task<IPAddress[]> GetHostAddressesAsync (string hostNameOrAddress)
502                 {
503                         return Task<IPAddress[]>.Factory.FromAsync (BeginGetHostAddresses, EndGetHostAddresses, hostNameOrAddress, null);
504                 }
505
506                 public static Task<IPHostEntry> GetHostEntryAsync (IPAddress address)
507                 {
508                         return Task<IPHostEntry>.Factory.FromAsync (BeginGetHostEntry, EndGetHostEntry, address, null);
509                 }
510
511                 public static Task<IPHostEntry> GetHostEntryAsync (string hostNameOrAddress)
512                 {
513                         return Task<IPHostEntry>.Factory.FromAsync (BeginGetHostEntry, EndGetHostEntry, hostNameOrAddress, null);
514                 }
515 #endif
516         }
517 }
518