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