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