* Dns.cs: Initial work on BeginGetHostByName and EndGetHostByName implemented.
[mono.git] / mcs / class / System / System.Net / Dns.cs
1 // System.Net.Dns.cs
2 //
3 // Author: Mads Pultz (mpultz@diku.dk)
4 //
5 // (C) Mads Pultz, 2001
6
7 using System;
8 using System.Net.Sockets;
9 using System.Text;
10 using System.Collections;
11 using System.Threading;
12 using System.Runtime.InteropServices;
13 using System.Runtime.Remoting.Messaging;
14
15 namespace System.Net {
16
17         public sealed class Dns {
18                 
19                 /// <summary>
20                 /// Helper class
21                 /// </summary>
22                 private sealed class DnsAsyncResult: IAsyncResult {
23                         private object state;
24                         private WaitHandle waitHandle;
25                         private bool completedSync, completed;
26                         private Worker worker;
27                 
28                         public DnsAsyncResult(object state) {
29                                 this.state = state;
30                                 waitHandle = new ManualResetEvent(false);
31                                 completedSync = completed = false;
32                         }       
33                         public object AsyncState {
34                                 get { return state; }
35                         }
36                         public WaitHandle AsyncWaitHandle {
37                                 set { waitHandle = value; }
38                                 get { return waitHandle; }
39                         }
40                         public bool CompletedSynchronously {
41                                 get { return completedSync; }
42                         }
43                         public bool IsCompleted {
44                                 set { completed = value; }
45                                 get { return completed; }
46                         }
47                         public Worker Worker {
48                                 set { worker = value; }
49                                 get { return worker; }
50                         }
51                 }
52
53                 /// <summary>
54                 /// Helper class for asynchronous calls to DNS server
55                 /// </summary>
56                 private sealed class Worker {
57                         private AsyncCallback reqCallback;
58                         private DnsAsyncResult reqRes;
59                         private string req;
60                         private IPHostEntry result;
61                         
62                         public Worker(string req, AsyncCallback reqCallback, DnsAsyncResult reqRes) {
63                                 this.req = req;
64                                 this.reqCallback = reqCallback;
65                                 this.reqRes = reqRes;
66                         }
67                         private void End() {
68                                 reqCallback(reqRes);
69                                 ((ManualResetEvent)reqRes.AsyncWaitHandle).Set();
70                                 reqRes.IsCompleted = true;
71                         }
72                         public void GetHostByName() {
73                                 lock(reqRes) {
74                                         result = Dns.GetHostByName(req);
75                                         End();
76                                 }
77                         }
78                         public void Resolve() {
79                                 lock(reqRes) {
80                                         result = Dns.Resolve(req);
81                                         End();
82                                 }
83                         }
84                         public IPHostEntry Result {
85                                 get { return result; }
86                         }
87                 }
88                 
89                 /// <summary>
90                 /// This class conforms to the C structure <c>hostent</c> and is used
91                 /// by the Dns class when doing native calls.
92                 /// </summary>
93                 [StructLayout(LayoutKind.Sequential)]
94                 private unsafe class Hostent {
95                         public string h_name;       /* official name */
96                         public byte** h_aliases;    /* alias list */
97                         public short h_addrtype;    /* address type */
98                         public short h_length;      /* address length */
99                         public byte** h_addr_list;  /* address list */
100                 }
101                 
102                 public static IAsyncResult BeginGetHostByName(string hostName,
103                                                   AsyncCallback requestCallback,
104                                                   object stateObject) {
105                         DnsAsyncResult requestResult = new DnsAsyncResult(stateObject);
106                         Worker worker = new Worker(hostName, requestCallback, requestResult);
107                         Thread child = new Thread(new ThreadStart(worker.GetHostByName));
108                         child.Start();
109                         return requestResult;
110                 }
111                 
112                 public static IAsyncResult BeginResolve(string hostName,
113                                                 AsyncCallback requestCallback,
114                                                 object stateObject) {
115                         // TODO
116                         throw new NotImplementedException();
117                 }
118                 
119                 public static IPHostEntry EndGetHostByName(IAsyncResult asyncResult) {
120                         return ((DnsAsyncResult)asyncResult).Worker.Result;
121                 }
122                 
123                 public static IPHostEntry EndResolve(IAsyncResult asyncResult) {
124                         // TODO
125                         throw new NotImplementedException();
126                 }
127                 
128                 /// <param name=hostName>
129                 /// IP address in network byte order (e.g. Big-Endian).
130                 /// </param>
131                 /// <param name=length>
132                 /// Length of IP address.
133                 /// </param>
134                 /// <param name=type>
135                 /// Type (should be 2, equals AF_INET).
136                 /// </param>
137                 [DllImport("cygwin1", EntryPoint="gethostbyaddr")]
138                 private static extern IntPtr _GetHostByAddress(byte[] hostName,
139                                                                short length,
140                                                                short type);
141                 
142                 /// <param name=address>
143                 /// IP address in network byte order (e.g. Big-Endian).
144                 /// </param>
145                 private static IPHostEntry GetHostByAddress(long address) {
146                         short length = 4;
147                         if (address > uint.MaxValue)
148                                 length = 8;
149                         byte[] addr = new byte[length];
150                         for(int i = length - 1, j = 0; i >= 0; --i, ++j) {
151                                 byte b = (byte)(address >> i * 8);
152 //                              Console.WriteLine(b);
153                                 addr[j] = b;
154                         }
155                         IntPtr p = _GetHostByAddress(addr, length, 2);  // TODO: set type
156                         if (p == IntPtr.Zero)
157                                 throw new SocketException();  // TODO: set error code
158                         Hostent h = new Hostent();
159                         System.Runtime.InteropServices.Marshal.PtrToStructure(p, h);
160                         return ToIPHostEntry(h);
161                 }
162                 
163                 public static IPHostEntry GetHostByAddress(IPAddress address) {
164                         if (address == null)
165                                 throw new ArgumentNullException();
166                         return GetHostByAddress(IPAddress.HostToNetworkOrder(address.Address));
167                 }
168                 
169                 public static IPHostEntry GetHostByAddress(string address) {
170                         if (address == null)
171                                 throw new ArgumentNullException();
172                         return GetHostByAddress(CreateAddress(address));
173                 }
174                 
175                 [DllImport("cygwin1", EntryPoint="gethostbyname")]
176                 private static extern IntPtr _GetHostByName(string hostName);
177                 
178                 public static IPHostEntry GetHostByName(string hostName) {
179                         if (hostName == null)
180                                 throw new ArgumentNullException();
181                         IntPtr p = _GetHostByName(hostName);
182                         //        int errNo = _h_errno;
183                         if (p == IntPtr.Zero)
184                                 throw new SocketException();  // TODO: set error code
185                         Hostent h = new Hostent();
186                         System.Runtime.InteropServices.Marshal.PtrToStructure(p, h);
187                         return ToIPHostEntry(h);
188                 }
189                 
190                 /// <summary>
191                 /// This method returns the host name associated with the local host.
192                 /// </summary>
193                 public static string GetHostName() {
194                         IPHostEntry h = GetHostByAddress("127.0.0.1");
195                         return h.HostName;
196                 }
197                 
198                 /// <param name=address>
199                 /// IP address in Little-Endian byte order.
200                 /// </param>
201                 /// <returns>
202                 /// IP address in dotted notation form.
203                 /// </returns>
204                 public static string IpToString(int address) {
205                         address = IPAddress.HostToNetworkOrder(address);
206                         StringBuilder res = new StringBuilder();
207                         for(int i = 3; i > 0; --i) {
208                                 byte b = (byte)(address >> i * 8);
209                                 res.Append(b);
210                                 res.Append('.');
211                         }
212                         res.Append((byte)address);
213                         return res.ToString();
214                 }
215                 
216                 /// <summary>
217                 /// This method resovles a DNS-style host name or IP
218                 /// address.
219                 /// </summary>
220                 /// <param name=hostName>
221                 /// A string containing either a DNS-style host name (e.g.
222                 /// www.go-mono.com) or IP address (e.g. 129.250.184.233).
223                 /// </param>
224                 public static IPHostEntry Resolve(string hostName) {
225                         if (hostName == null)
226                                 throw new ArgumentNullException();
227                         try {
228                                 long addr = CreateAddress(hostName);
229                                 if (addr > uint.MaxValue)
230                                         throw new FormatException("Only IP version 4 addresses are supported");
231                                 return GetHostByAddress(addr);
232                         } catch (FormatException) {
233                           return GetHostByName(hostName);
234                         }
235                 }
236                 
237                 /// <summary>
238                 /// Utility method. This method converts a Hostent instance to a
239                 /// IPHostEntry instance.
240                 /// </summary>
241                 /// <param name=h>
242                 /// Object which should be mapped to a IPHostEntry instance.
243                 /// </param>
244                 private static unsafe IPHostEntry ToIPHostEntry(Hostent h) {
245                         IPHostEntry res = new IPHostEntry();
246                         
247                         // Set host name
248                         res.HostName = h.h_name;
249                         
250                         // Set IP address list
251                         byte** p = h.h_addr_list;
252                         ArrayList tmp = new ArrayList(1);
253                         while (*p != null) {
254                                 tmp.Add(CreateIPAddress(*p, h.h_length));
255                                 ++p;
256                         }
257                         IPAddress[] addr_list = new IPAddress[tmp.Count];
258                         for(int i = 0; i < tmp.Count; ++i)
259                                 addr_list[i] = (IPAddress)tmp[i];
260                         res.AddressList = addr_list;
261                         
262                         // Set IP aliases
263                         p = h.h_aliases;
264                         tmp.Clear();
265                         while (*p != null) {
266                                 tmp.Add(new string((sbyte*)*p));
267                                 ++p;
268                         }
269                         string[] aliases = new string[tmp.Count];
270                         for(int i = 0; i < tmp.Count; ++i)
271                                 aliases[i] = (string)tmp[i];
272                         res.Aliases = aliases;
273                         
274                         return res;
275                 }
276                 
277                 /// <summary>
278                 /// Utility method. Convert IP address in dotted notation
279                 /// to IP address.
280                 /// </summary>
281                 private static long CreateAddress(string address) {
282                         string[] tokens = address.Split('.');
283                         if (tokens.Length % 4 != 0)
284                                 throw new FormatException("IP address has invalid length");
285                         long addr = 0;
286                         for(int i = 0, j = tokens.Length - 1; i < tokens.Length; ++i, --j) {
287                                 try {
288                                         addr = addr | (((long)byte.Parse(tokens[i])) << j * 8);
289                                 } catch (OverflowException) {
290                                         throw new FormatException("Invalid IP address format");
291                                 }
292                         }
293                         return addr;
294                 }
295         
296                 /// <summary>
297                 /// Utility method. This method creates a IP address.
298                 /// </summary>
299                 /// <param name=addr>
300                 /// IP address in network byte order (e.g. Big-Endian).
301                 /// </param>
302                 /// <param name=length>
303                 /// Length of IP address (4 or 8 bytes).
304                 /// </param>
305                 private static unsafe IPAddress CreateIPAddress(byte* addr, short length) {
306                         byte* p = addr;
307                         long res = 0;
308                         for(int i = 0, j = length - 1; i < length; ++i, --j) {
309                                 res += *p << j * 8;
310                                 ++p;
311                         }
312                         if (res > uint.MaxValue)
313                                 return new IPAddress(IPAddress.NetworkToHostOrder(res));
314                         else
315                                 return new IPAddress(IPAddress.NetworkToHostOrder((int)res));
316                 }
317         }
318 }
319