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