fb532cd22c2a8a19929b6cfe1b85a20f08d00f7d
[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                                 Error_11001 (entry.HostName);
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                 static void Error_11001 (string hostName)
297                 {
298                         throw new SocketException(11001, string.Format ("Could not resolve host '{0}'", hostName));
299
300                 }
301
302                 private static IPHostEntry hostent_to_IPHostEntry(string originalHostName, string h_name, string[] h_aliases, string[] h_addrlist) 
303                 {
304                         IPHostEntry he = new IPHostEntry();
305                         ArrayList addrlist = new ArrayList();
306
307                         he.HostName = h_name;
308                         he.Aliases = h_aliases;
309                         for(int i=0; i<h_addrlist.Length; i++) {
310                                 try {
311                                         IPAddress newAddress = IPAddress.Parse(h_addrlist[i]);
312
313                                         if( (Socket.SupportsIPv6 && newAddress.AddressFamily == AddressFamily.InterNetworkV6) ||
314                                             (Socket.SupportsIPv4 && newAddress.AddressFamily == AddressFamily.InterNetwork) )
315                                                 addrlist.Add(newAddress);
316                                 } catch (ArgumentNullException) {
317                                         /* Ignore this, as the
318                                          * internal call might have
319                                          * left some blank entries at
320                                          * the end of the array
321                                          */
322                                 }
323                         }
324
325                         if(addrlist.Count == 0)
326                                 Error_11001 (originalHostName);
327
328                         he.AddressList = addrlist.ToArray(typeof(IPAddress)) as IPAddress[];
329                         return he;
330                 }
331
332                 [Obsolete ("Use GetHostEntry instead")]
333                 public static IPHostEntry GetHostByAddress(IPAddress address)
334                 {
335                         if (address == null)
336                                 throw new ArgumentNullException ("address");
337
338                         return GetHostByAddressFromString (address.ToString (), false);
339                 }
340
341                 [Obsolete ("Use GetHostEntry instead")]
342                 public static IPHostEntry GetHostByAddress(string address)
343                 {
344                         if (address == null)
345                                 throw new ArgumentNullException ("address");
346
347                         return GetHostByAddressFromString (address, true);
348                 }
349
350                 static IPHostEntry GetHostByAddressFromString (string address, bool parse)
351                 {
352                         // Undocumented MS behavior: when called with IF_ANY,
353                         // this should return the local host
354                         if (address.Equals ("0.0.0.0")) {
355                                 address = "127.0.0.1";
356                                 parse = false;
357                         }
358
359                         // Must check the IP format, might send an exception if invalid string.
360                         if (parse)
361                                 IPAddress.Parse (address);
362
363                         string h_name;
364                         string[] h_aliases, h_addrlist;
365 #if TARGET_JVM
366                         h_name = null;
367                         h_aliases = null;
368                         h_addrlist = null;
369                         try {
370                                 java.net.InetAddress[] iaArr = 
371                                         java.net.InetAddress.getAllByName(address);
372                                 if (iaArr != null && iaArr.Length > 0)
373                                     h_name = iaArr[0].getHostName();
374                                 if (iaArr != null && iaArr.Length > 0)
375                                 {
376                                     h_addrlist = new String[iaArr.Length];
377                                     for (int i = 0; i < h_addrlist.Length; i++)
378                                         h_addrlist[i] = iaArr[i].getHostAddress();
379                                 }
380                         } catch (java.net.UnknownHostException jUHE) {
381                                 throw new SocketException((int)SocketError.HostNotFound, jUHE.Message);
382                         }
383 #else
384                         bool ret = GetHostByAddr_internal(address, out h_name, out h_aliases, out h_addrlist);
385                         if (!ret)
386                                 Error_11001 (address);
387 #endif
388                         return (hostent_to_IPHostEntry (address, h_name, h_aliases, h_addrlist));
389                         
390                 }
391
392                 public static IPHostEntry GetHostEntry (string hostNameOrAddress)
393                 {
394                         if (hostNameOrAddress == null)
395                                 throw new ArgumentNullException ("hostNameOrAddress");
396                         if (hostNameOrAddress == "0.0.0.0" || hostNameOrAddress == "::0")
397                                 throw new ArgumentException ("Addresses 0.0.0.0 (IPv4) " +
398                                         "and ::0 (IPv6) are unspecified addresses. You " +
399                                         "cannot use them as target address.",
400                                         "hostNameOrAddress");
401
402                         IPAddress addr;
403                         if (hostNameOrAddress.Length > 0 && IPAddress.TryParse (hostNameOrAddress, out addr))
404                                 return GetHostEntry (addr);
405
406                         return GetHostByName (hostNameOrAddress);
407                 }
408
409                 public static IPHostEntry GetHostEntry (IPAddress address)
410                 {
411                         if (address == null)
412                                 throw new ArgumentNullException ("address");
413
414                         return GetHostByAddressFromString (address.ToString (), false);
415                 }
416
417                 public static IPAddress [] GetHostAddresses (string hostNameOrAddress)
418                 {
419                         if (hostNameOrAddress == null)
420                                 throw new ArgumentNullException ("hostNameOrAddress");
421
422                         if (hostNameOrAddress == "0.0.0.0" || hostNameOrAddress == "::0")
423                                 throw new ArgumentException ("Addresses 0.0.0.0 (IPv4) " +
424                                         "and ::0 (IPv6) are unspecified addresses. You " +
425                                         "cannot use them as target address.",
426                                         "hostNameOrAddress");
427
428                         IPAddress addr;
429                         if (hostNameOrAddress.Length > 0 && IPAddress.TryParse (hostNameOrAddress, out addr))
430                                 return new IPAddress[1] { addr };
431
432                         return GetHostEntry (hostNameOrAddress).AddressList;
433                 }
434
435                 [Obsolete ("Use GetHostEntry instead")]
436                 public static IPHostEntry GetHostByName (string hostName)
437                 {
438                         if (hostName == null)
439                                 throw new ArgumentNullException ("hostName");
440 #if TARGET_JVM
441                         if (hostName.Length == 0)
442                                 hostName = "localhost";
443                         try {
444                                 java.net.InetAddress[] iaArr = java.net.InetAddress.getAllByName(hostName);
445                                 IPHostEntry host = new IPHostEntry();
446                                 if (iaArr != null && iaArr.Length > 0)
447                                 {
448                                         host.HostName = iaArr[0].getHostName();
449                                         IPAddress[] ipArr = new IPAddress[iaArr.Length];
450                                         for (int i = 0; i < iaArr.Length; i++)
451                                                 ipArr[i] = IPAddress.Parse(iaArr[i].getHostAddress());
452
453                                         host.AddressList = ipArr;
454                                 }
455                                 return host;
456                         } catch (java.net.UnknownHostException jUHE) {
457                                 throw new SocketException((int)SocketError.HostNotFound, jUHE.Message);
458                         }
459 #else
460                         string h_name;
461                         string[] h_aliases, h_addrlist;
462
463                         bool ret = GetHostByName_internal(hostName, out h_name, out h_aliases, out h_addrlist);
464                         if (ret == false)
465                                 Error_11001 (hostName);
466
467                         return(hostent_to_IPHostEntry(hostName, h_name, h_aliases, h_addrlist));
468 #endif
469                 }
470
471                 public static string GetHostName ()
472                 {
473 #if TARGET_JVM
474                         return java.net.InetAddress.getLocalHost ().getHostName ();
475 #else
476                         string hostName;
477
478                         bool ret = GetHostName_internal(out hostName);
479
480                         if (ret == false)
481                                 Error_11001 (hostName);
482
483                         return hostName;
484 #endif
485                 }
486
487                 [Obsolete ("Use GetHostEntry instead")]
488                 public static IPHostEntry Resolve(string hostName) 
489                 {
490                         if (hostName == null)
491                                 throw new ArgumentNullException ("hostName");
492
493                         IPHostEntry ret = null;
494
495                         try {
496                                 ret =  GetHostByAddress(hostName);
497                         }
498                         catch{}
499
500                         if(ret == null)
501                                 ret =  GetHostByName(hostName);
502
503                         return ret;
504                 }
505
506 #if NET_4_5
507                 public static Task<IPAddress[]> GetHostAddressesAsync (string hostNameOrAddress)
508                 {
509                         return Task<IPAddress[]>.Factory.FromAsync (BeginGetHostAddresses, EndGetHostAddresses, hostNameOrAddress, null);
510                 }
511
512                 public static Task<IPHostEntry> GetHostEntryAsync (IPAddress address)
513                 {
514                         return Task<IPHostEntry>.Factory.FromAsync (BeginGetHostEntry, EndGetHostEntry, address, null);
515                 }
516
517                 public static Task<IPHostEntry> GetHostEntryAsync (string hostNameOrAddress)
518                 {
519                         return Task<IPHostEntry>.Factory.FromAsync (BeginGetHostEntry, EndGetHostEntry, hostNameOrAddress, null);
520                 }
521 #endif
522         }
523 }
524