Merge pull request #185 from QuickJack/master
[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 #if !MOONLIGHT // global remove of async methods
78
79
80                 private delegate IPHostEntry GetHostByNameCallback (string hostName);
81                 private delegate IPHostEntry ResolveCallback (string hostName);
82                 private delegate IPHostEntry GetHostEntryNameCallback (string hostName);
83                 private delegate IPHostEntry GetHostEntryIPCallback (IPAddress hostAddress);
84                 private delegate IPAddress [] GetHostAddressesCallback (string hostName);
85
86 #if !MOBILE
87                 static void OnCompleted (object sender, SimpleResolverEventArgs e)
88                 {
89                         DnsAsyncResult ares = (DnsAsyncResult) e.UserToken;
90                         IPHostEntry entry = e.HostEntry;
91                         if (entry == null || e.ResolverError != 0) {
92                                 ares.SetCompleted (false, new Exception ("Error: " + e.ResolverError));
93                                 return;
94                         }
95                         ares.SetCompleted (false, entry);
96                 }
97
98                 static IAsyncResult BeginAsyncCallAddresses (string host, AsyncCallback callback, object state)
99                 {
100                         SimpleResolverEventArgs e = new SimpleResolverEventArgs ();
101                         e.Completed += OnCompleted;
102                         e.HostName = host;
103                         DnsAsyncResult ares = new DnsAsyncResult (callback, state);
104                         e.UserToken = ares;
105                         if (resolver.GetHostAddressesAsync (e) == false)
106                                 ares.SetCompleted (true, e.HostEntry); // Completed synchronously
107                         return ares;
108                 }
109
110                 static IAsyncResult BeginAsyncCall (string host, AsyncCallback callback, object state)
111                 {
112                         SimpleResolverEventArgs e = new SimpleResolverEventArgs ();
113                         e.Completed += OnCompleted;
114                         e.HostName = host;
115                         DnsAsyncResult ares = new DnsAsyncResult (callback, state);
116                         e.UserToken = ares;
117                         if (resolver.GetHostEntryAsync (e) == false)
118                                 ares.SetCompleted (true, e.HostEntry); // Completed synchronously
119                         return ares;
120                 }
121
122                 static IPHostEntry EndAsyncCall (DnsAsyncResult ares)
123                 {
124                         if (ares == null)
125                                 throw new ArgumentException ("Invalid asyncResult");
126                         if (!ares.IsCompleted)
127                                 ares.AsyncWaitHandle.WaitOne ();
128                         if (ares.Exception != null)
129                                 throw ares.Exception;
130                         IPHostEntry entry = ares.HostEntry;
131                         if (entry == null || entry.AddressList == null || entry.AddressList.Length == 0)
132                                 throw new SocketException(11001);
133                         return entry;
134                 }
135 #endif
136
137                 [Obsolete ("Use BeginGetHostEntry instead")]
138                 public static IAsyncResult BeginGetHostByName (string hostName, AsyncCallback requestCallback, object stateObject)
139                 {
140                         if (hostName == null)
141                                 throw new ArgumentNullException ("hostName");
142
143 #if !MOBILE
144                         if (use_mono_dns)
145                                 return BeginAsyncCall (hostName, requestCallback, stateObject);
146 #endif
147
148                         GetHostByNameCallback c = new GetHostByNameCallback (GetHostByName);
149                         return c.BeginInvoke (hostName, requestCallback, stateObject);
150                 }
151
152                 [Obsolete ("Use BeginGetHostEntry instead")]
153                 public static IAsyncResult BeginResolve (string hostName, AsyncCallback requestCallback, object stateObject)
154                 {
155                         if (hostName == null)
156                                 throw new ArgumentNullException ("hostName");
157
158 #if !MOBILE
159                         if (use_mono_dns)
160                                 return BeginAsyncCall (hostName, requestCallback, stateObject);
161 #endif
162
163                         ResolveCallback c = new ResolveCallback (Resolve);
164                         return c.BeginInvoke (hostName, requestCallback, stateObject);
165                 }
166
167                 public static IAsyncResult BeginGetHostAddresses (string hostNameOrAddress, AsyncCallback requestCallback, object stateObject)
168                 {
169                         if (hostNameOrAddress == null)
170                                 throw new ArgumentNullException ("hostName");
171                         if (hostNameOrAddress == "0.0.0.0" || hostNameOrAddress == "::0")
172                                 throw new ArgumentException ("Addresses 0.0.0.0 (IPv4) " +
173                                         "and ::0 (IPv6) are unspecified addresses. You " +
174                                         "cannot use them as target address.",
175                                         "hostNameOrAddress");
176
177 #if !MOBILE
178                         if (use_mono_dns)
179                                 return BeginAsyncCallAddresses (hostNameOrAddress, requestCallback, stateObject);
180 #endif
181
182                         GetHostAddressesCallback c = new GetHostAddressesCallback (GetHostAddresses);
183                         return c.BeginInvoke (hostNameOrAddress, requestCallback, stateObject);
184                 }
185
186                 public static IAsyncResult BeginGetHostEntry (string hostNameOrAddress, AsyncCallback requestCallback, object stateObject)
187                 {
188                         if (hostNameOrAddress == null)
189                                 throw new ArgumentNullException ("hostName");
190                         if (hostNameOrAddress == "0.0.0.0" || hostNameOrAddress == "::0")
191                                 throw new ArgumentException ("Addresses 0.0.0.0 (IPv4) " +
192                                         "and ::0 (IPv6) are unspecified addresses. You " +
193                                         "cannot use them as target address.",
194                                         "hostNameOrAddress");
195
196 #if !MOBILE
197                         if (use_mono_dns)
198                                 return BeginAsyncCall (hostNameOrAddress, requestCallback, stateObject);
199 #endif
200
201                         GetHostEntryNameCallback c = new GetHostEntryNameCallback (GetHostEntry);
202                         return c.BeginInvoke (hostNameOrAddress, requestCallback, stateObject);
203                 }
204
205                 public static IAsyncResult BeginGetHostEntry (IPAddress address, AsyncCallback requestCallback, object stateObject)
206                 {
207                         if (address == null)
208                                 throw new ArgumentNullException ("address");
209
210 #if !MOBILE
211                         if (use_mono_dns)
212                                 return BeginAsyncCall (address.ToString (), requestCallback, stateObject);
213 #endif
214
215                         GetHostEntryIPCallback c = new GetHostEntryIPCallback (GetHostEntry);
216                         return c.BeginInvoke (address, requestCallback, stateObject);
217                 }
218
219                 [Obsolete ("Use EndGetHostEntry instead")]
220                 public static IPHostEntry EndGetHostByName (IAsyncResult asyncResult) 
221                 {
222                         if (asyncResult == null)
223                                 throw new ArgumentNullException ("asyncResult");
224
225 #if !MOBILE
226                         if (use_mono_dns)
227                                 return EndAsyncCall (asyncResult as DnsAsyncResult);
228 #endif
229
230                         AsyncResult async = (AsyncResult) asyncResult;
231                         GetHostByNameCallback cb = (GetHostByNameCallback) async.AsyncDelegate;
232                         return cb.EndInvoke(asyncResult);
233                 }
234
235                 [Obsolete ("Use EndGetHostEntry instead")]
236                 public static IPHostEntry EndResolve (IAsyncResult asyncResult) 
237                 {
238                         if (asyncResult == null)
239                                 throw new ArgumentNullException ("asyncResult");
240
241 #if !MOBILE
242                         if (use_mono_dns)
243                                 return EndAsyncCall (asyncResult as DnsAsyncResult);
244 #endif
245
246                         AsyncResult async = (AsyncResult) asyncResult;
247                         ResolveCallback cb = (ResolveCallback) async.AsyncDelegate;
248                         return cb.EndInvoke(asyncResult);
249                 }
250
251                 public static IPAddress [] EndGetHostAddresses (IAsyncResult asyncResult) 
252                 {
253                         if (asyncResult == null)
254                                 throw new ArgumentNullException ("asyncResult");
255
256 #if !MOBILE
257                         if (use_mono_dns) {
258                                 IPHostEntry entry = EndAsyncCall (asyncResult as DnsAsyncResult);
259                                 if (entry == null)
260                                         return null;
261                                 return entry.AddressList;
262                         }
263 #endif
264
265                         AsyncResult async = (AsyncResult) asyncResult;
266                         GetHostAddressesCallback cb = (GetHostAddressesCallback) async.AsyncDelegate;
267                         return cb.EndInvoke(asyncResult);
268                 }
269
270                 public static IPHostEntry EndGetHostEntry (IAsyncResult asyncResult) 
271                 {
272                         if (asyncResult == null)
273                                 throw new ArgumentNullException ("asyncResult");
274
275 #if !MOBILE
276                         if (use_mono_dns)
277                                 return EndAsyncCall (asyncResult as DnsAsyncResult);
278 #endif
279
280                         AsyncResult async = (AsyncResult) asyncResult;
281                         if (async.AsyncDelegate is GetHostEntryIPCallback)
282                                 return ((GetHostEntryIPCallback) async.AsyncDelegate).EndInvoke (asyncResult);
283                         GetHostEntryNameCallback cb = (GetHostEntryNameCallback) async.AsyncDelegate;
284                         return cb.EndInvoke(asyncResult);
285                 }
286                 
287 #endif // !MOONLIGHT: global remove of async methods
288
289 #if !TARGET_JVM
290                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
291                 private extern static bool GetHostByName_internal(string host, out string h_name, out string[] h_aliases, out string[] h_addr_list);
292
293                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
294                 private extern static bool GetHostByAddr_internal(string addr, out string h_name, out string[] h_aliases, out string[] h_addr_list);
295
296                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
297                 private extern static bool GetHostName_internal(out string h_name);
298 #endif  
299
300                 private static IPHostEntry hostent_to_IPHostEntry(string h_name, string[] h_aliases, string[] h_addrlist) 
301                 {
302                         IPHostEntry he = new IPHostEntry();
303                         ArrayList addrlist = new ArrayList();
304
305                         he.HostName = h_name;
306                         he.Aliases = h_aliases;
307                         for(int i=0; i<h_addrlist.Length; i++) {
308                                 try {
309                                         IPAddress newAddress = IPAddress.Parse(h_addrlist[i]);
310
311                                         if( (Socket.SupportsIPv6 && newAddress.AddressFamily == AddressFamily.InterNetworkV6) ||
312                                             (Socket.SupportsIPv4 && newAddress.AddressFamily == AddressFamily.InterNetwork) )
313                                                 addrlist.Add(newAddress);
314                                 } catch (ArgumentNullException) {
315                                         /* Ignore this, as the
316                                          * internal call might have
317                                          * left some blank entries at
318                                          * the end of the array
319                                          */
320                                 }
321                         }
322
323                         if(addrlist.Count == 0)
324                                 throw new SocketException(11001);
325
326                         he.AddressList = addrlist.ToArray(typeof(IPAddress)) as IPAddress[];
327                         return he;
328                 }
329
330                 [Obsolete ("Use GetHostEntry instead")]
331                 public static IPHostEntry GetHostByAddress(IPAddress address)
332                 {
333                         if (address == null)
334                                 throw new ArgumentNullException ("address");
335
336                         return GetHostByAddressFromString (address.ToString (), false);
337                 }
338
339                 [Obsolete ("Use GetHostEntry instead")]
340                 public static IPHostEntry GetHostByAddress(string address)
341                 {
342                         if (address == null)
343                                 throw new ArgumentNullException ("address");
344
345                         return GetHostByAddressFromString (address, true);
346                 }
347
348                 static IPHostEntry GetHostByAddressFromString (string address, bool parse)
349                 {
350                         // Undocumented MS behavior: when called with IF_ANY,
351                         // this should return the local host
352                         if (address.Equals ("0.0.0.0")) {
353                                 address = "127.0.0.1";
354                                 parse = false;
355                         }
356
357                         // Must check the IP format, might send an exception if invalid string.
358                         if (parse)
359                                 IPAddress.Parse (address);
360
361                         string h_name;
362                         string[] h_aliases, h_addrlist;
363 #if TARGET_JVM
364                         h_name = null;
365                         h_aliases = null;
366                         h_addrlist = null;
367                         try {
368                                 java.net.InetAddress[] iaArr = 
369                                         java.net.InetAddress.getAllByName(address);
370                                 if (iaArr != null && iaArr.Length > 0)
371                                     h_name = iaArr[0].getHostName();
372                                 if (iaArr != null && iaArr.Length > 0)
373                                 {
374                                     h_addrlist = new String[iaArr.Length];
375                                     for (int i = 0; i < h_addrlist.Length; i++)
376                                         h_addrlist[i] = iaArr[i].getHostAddress();
377                                 }
378                         } catch (java.net.UnknownHostException jUHE) {
379                                 throw new SocketException((int)SocketError.HostNotFound, jUHE.Message);
380                         }
381 #else
382                         bool ret = GetHostByAddr_internal(address, out h_name, out h_aliases, out h_addrlist);
383                         if (!ret)
384                                 throw new SocketException(11001);
385 #endif
386                         return (hostent_to_IPHostEntry (h_name, h_aliases, h_addrlist));
387                         
388                 }
389
390                 public static IPHostEntry GetHostEntry (string hostNameOrAddress)
391                 {
392                         if (hostNameOrAddress == null)
393                                 throw new ArgumentNullException ("hostNameOrAddress");
394                         if (hostNameOrAddress == "0.0.0.0" || hostNameOrAddress == "::0")
395                                 throw new ArgumentException ("Addresses 0.0.0.0 (IPv4) " +
396                                         "and ::0 (IPv6) are unspecified addresses. You " +
397                                         "cannot use them as target address.",
398                                         "hostNameOrAddress");
399
400                         IPAddress addr;
401                         if (hostNameOrAddress.Length > 0 && IPAddress.TryParse (hostNameOrAddress, out addr))
402                                 return GetHostEntry (addr);
403
404                         return GetHostByName (hostNameOrAddress);
405                 }
406
407                 public static IPHostEntry GetHostEntry (IPAddress address)
408                 {
409                         if (address == null)
410                                 throw new ArgumentNullException ("address");
411
412                         return GetHostByAddressFromString (address.ToString (), false);
413                 }
414
415                 public static IPAddress [] GetHostAddresses (string hostNameOrAddress)
416                 {
417                         if (hostNameOrAddress == null)
418                                 throw new ArgumentNullException ("hostNameOrAddress");
419
420                         if (hostNameOrAddress == "0.0.0.0" || hostNameOrAddress == "::0")
421                                 throw new ArgumentException ("Addresses 0.0.0.0 (IPv4) " +
422                                         "and ::0 (IPv6) are unspecified addresses. You " +
423                                         "cannot use them as target address.",
424                                         "hostNameOrAddress");
425
426                         IPAddress addr;
427                         if (hostNameOrAddress.Length > 0 && IPAddress.TryParse (hostNameOrAddress, out addr))
428                                 return new IPAddress[1] { addr };
429
430                         return GetHostEntry (hostNameOrAddress).AddressList;
431                 }
432
433                 [Obsolete ("Use GetHostEntry instead")]
434                 public static IPHostEntry GetHostByName (string hostName)
435                 {
436                         if (hostName == null)
437                                 throw new ArgumentNullException ("hostName");
438 #if TARGET_JVM
439                         if (hostName.Length == 0)
440                                 hostName = "localhost";
441                         try {
442                                 java.net.InetAddress[] iaArr = java.net.InetAddress.getAllByName(hostName);
443                                 IPHostEntry host = new IPHostEntry();
444                                 if (iaArr != null && iaArr.Length > 0)
445                                 {
446                                         host.HostName = iaArr[0].getHostName();
447                                         IPAddress[] ipArr = new IPAddress[iaArr.Length];
448                                         for (int i = 0; i < iaArr.Length; i++)
449                                                 ipArr[i] = IPAddress.Parse(iaArr[i].getHostAddress());
450
451                                         host.AddressList = ipArr;
452                                 }
453                                 return host;
454                         } catch (java.net.UnknownHostException jUHE) {
455                                 throw new SocketException((int)SocketError.HostNotFound, jUHE.Message);
456                         }
457 #else
458                         string h_name;
459                         string[] h_aliases, h_addrlist;
460
461                         bool ret = GetHostByName_internal(hostName, out h_name, out h_aliases, out h_addrlist);
462                         if (ret == false)
463                                 throw new SocketException(11001);
464
465                         return(hostent_to_IPHostEntry(h_name, h_aliases, h_addrlist));
466 #endif
467                 }
468
469                 public static string GetHostName ()
470                 {
471 #if TARGET_JVM
472                         return java.net.InetAddress.getLocalHost ().getHostName ();
473 #else
474                         string hostName;
475
476                         bool ret = GetHostName_internal(out hostName);
477
478                         if (ret == false)
479                                 throw new SocketException(11001);
480
481                         return hostName;
482 #endif
483                 }
484
485                 [Obsolete ("Use GetHostEntry instead")]
486                 public static IPHostEntry Resolve(string hostName) 
487                 {
488                         if (hostName == null)
489                                 throw new ArgumentNullException ("hostName");
490
491                         IPHostEntry ret = null;
492
493                         try {
494                                 ret =  GetHostByAddress(hostName);
495                         }
496                         catch{}
497
498                         if(ret == null)
499                                 ret =  GetHostByName(hostName);
500
501                         return ret;
502                 }
503
504 #if NET_4_5
505                 public static Task<IPAddress[]> GetHostAddressesAsync (string hostNameOrAddress)
506                 {
507                         return Task<IPAddress[]>.Factory.FromAsync (BeginGetHostAddresses, EndGetHostAddresses, hostNameOrAddress, null);
508                 }
509
510                 public static Task<IPHostEntry> GetHostEntryAsync (IPAddress address)
511                 {
512                         return Task<IPHostEntry>.Factory.FromAsync (BeginGetHostEntry, EndGetHostEntry, address, null);
513                 }
514
515                 public static Task<IPHostEntry> GetHostEntryAsync (string hostNameOrAddress)
516                 {
517                         return Task<IPHostEntry>.Factory.FromAsync (BeginGetHostEntry, EndGetHostEntry, hostNameOrAddress, null);
518                 }
519 #endif
520         }
521 }
522