2002-01-06 Ravi Pratap <ravi@ximian.com>
[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                 [MonoTODO]
113                 public static IAsyncResult BeginResolve(string hostName,
114                                                 AsyncCallback requestCallback,
115                                                 object stateObject) {
116                         // TODO
117                         throw new NotImplementedException();
118                 }
119                 
120                 public static IPHostEntry EndGetHostByName(IAsyncResult asyncResult) {
121                         return ((DnsAsyncResult)asyncResult).Worker.Result;
122                 }
123
124                 [MonoTODO]
125                 public static IPHostEntry EndResolve(IAsyncResult asyncResult) {
126                         // TODO
127                         throw new NotImplementedException();
128                 }
129                 
130                 /// <param name=hostName>
131                 /// IP address in network byte order (e.g. Big-Endian).
132                 /// </param>
133                 /// <param name=length>
134                 /// Length of IP address.
135                 /// </param>
136                 /// <param name=type>
137                 /// Type (should be 2, equals AF_INET).
138                 /// </param>
139                 [DllImport("cygwin1", EntryPoint="gethostbyaddr")]
140                 private static extern IntPtr _GetHostByAddress(byte[] hostName,
141                                                                short length,
142                                                                short type);
143                 
144                 /// <param name=address>
145                 /// IP address in network byte order (e.g. Big-Endian).
146                 /// </param>
147                 [MonoTODO]
148                 private static IPHostEntry GetHostByAddress(long address) {
149                         short length = 4;
150                         if (address > uint.MaxValue)
151                                 length = 8;
152                         byte[] addr = new byte[length];
153                         for(int i = length - 1, j = 0; i >= 0; --i, ++j) {
154                                 byte b = (byte)(address >> i * 8);
155 //                              Console.WriteLine(b);
156                                 addr[j] = b;
157                         }
158                         IntPtr p = _GetHostByAddress(addr, length, 2);  // TODO: set type
159                         if (p == IntPtr.Zero)
160                                 throw new SocketException();  // TODO: set error code
161                         Hostent h = new Hostent();
162                         System.Runtime.InteropServices.Marshal.PtrToStructure(p, h);
163                         return ToIPHostEntry(h);
164                 }
165                 
166                 public static IPHostEntry GetHostByAddress(IPAddress address) {
167                         if (address == null)
168                                 throw new ArgumentNullException();
169                         return GetHostByAddress(IPAddress.HostToNetworkOrder(address.Address));
170                 }
171                 
172                 public static IPHostEntry GetHostByAddress(string address) {
173                         if (address == null)
174                                 throw new ArgumentNullException();
175                         return GetHostByAddress(CreateAddress(address));
176                 }
177                 
178                 [DllImport("cygwin1", EntryPoint="gethostbyname")]
179                 private static extern IntPtr _GetHostByName(string hostName);
180
181                 [MonoTODO]
182                 public static IPHostEntry GetHostByName(string hostName) {
183                         if (hostName == null)
184                                 throw new ArgumentNullException();
185                         IntPtr p = _GetHostByName(hostName);
186                         //        int errNo = _h_errno;
187                         if (p == IntPtr.Zero)
188                                 throw new SocketException();  // TODO: set error code
189                         Hostent h = new Hostent();
190                         System.Runtime.InteropServices.Marshal.PtrToStructure(p, h);
191                         return ToIPHostEntry(h);
192                 }
193                 
194                 /// <summary>
195                 /// This method returns the host name associated with the local host.
196                 /// </summary>
197                 public static string GetHostName() {
198                         IPHostEntry h = GetHostByAddress("127.0.0.1");
199                         return h.HostName;
200                 }
201                 
202                 /// <param name=address>
203                 /// IP address in Little-Endian byte order.
204                 /// </param>
205                 /// <returns>
206                 /// IP address in dotted notation form.
207                 /// </returns>
208                 public static string IpToString(int address) {
209                         address = IPAddress.HostToNetworkOrder(address);
210                         StringBuilder res = new StringBuilder();
211                         for(int i = 3; i > 0; --i) {
212                                 byte b = (byte)(address >> i * 8);
213                                 res.Append(b);
214                                 res.Append('.');
215                         }
216                         res.Append((byte)address);
217                         return res.ToString();
218                 }
219                 
220                 /// <summary>
221                 /// This method resovles a DNS-style host name or IP
222                 /// address.
223                 /// </summary>
224                 /// <param name=hostName>
225                 /// A string containing either a DNS-style host name (e.g.
226                 /// www.go-mono.com) or IP address (e.g. 129.250.184.233).
227                 /// </param>
228                 public static IPHostEntry Resolve(string hostName) {
229                         if (hostName == null)
230                                 throw new ArgumentNullException();
231                         try {
232                                 long addr = CreateAddress(hostName);
233                                 if (addr > uint.MaxValue)
234                                         throw new FormatException("Only IP version 4 addresses are supported");
235                                 return GetHostByAddress(addr);
236                         } catch (FormatException) {
237                           return GetHostByName(hostName);
238                         }
239                 }
240                 
241                 /// <summary>
242                 /// Utility method. This method converts a Hostent instance to a
243                 /// IPHostEntry instance.
244                 /// </summary>
245                 /// <param name=h>
246                 /// Object which should be mapped to a IPHostEntry instance.
247                 /// </param>
248                 private static unsafe IPHostEntry ToIPHostEntry(Hostent h) {
249                         IPHostEntry res = new IPHostEntry();
250                         
251                         // Set host name
252                         res.HostName = h.h_name;
253                         
254                         // Set IP address list
255                         byte** p = h.h_addr_list;
256                         ArrayList tmp = new ArrayList(1);
257                         while (*p != null) {
258                                 tmp.Add(CreateIPAddress(*p, h.h_length));
259                                 ++p;
260                         }
261                         IPAddress[] addr_list = new IPAddress[tmp.Count];
262                         for(int i = 0; i < tmp.Count; ++i)
263                                 addr_list[i] = (IPAddress)tmp[i];
264                         res.AddressList = addr_list;
265                         
266                         // Set IP aliases
267                         p = h.h_aliases;
268                         tmp.Clear();
269                         while (*p != null) {
270                                 tmp.Add(new string((sbyte*)*p));
271                                 ++p;
272                         }
273                         string[] aliases = new string[tmp.Count];
274                         for(int i = 0; i < tmp.Count; ++i)
275                                 aliases[i] = (string)tmp[i];
276                         res.Aliases = aliases;
277                         
278                         return res;
279                 }
280                 
281                 /// <summary>
282                 /// Utility method. Convert IP address in dotted notation
283                 /// to IP address.
284                 /// </summary>
285                 private static long CreateAddress(string address) {
286                         string[] tokens = address.Split('.');
287                         if (tokens.Length % 4 != 0)
288                                 throw new FormatException("IP address has invalid length");
289                         long addr = 0;
290                         for(int i = 0, j = tokens.Length - 1; i < tokens.Length; ++i, --j) {
291                                 try {
292                                         addr = addr | (((long)byte.Parse(tokens[i])) << j * 8);
293                                 } catch (OverflowException) {
294                                         throw new FormatException("Invalid IP address format");
295                                 }
296                         }
297                         return addr;
298                 }
299         
300                 /// <summary>
301                 /// Utility method. This method creates a IP address.
302                 /// </summary>
303                 /// <param name=addr>
304                 /// IP address in network byte order (e.g. Big-Endian).
305                 /// </param>
306                 /// <param name=length>
307                 /// Length of IP address (4 or 8 bytes).
308                 /// </param>
309                 private static unsafe IPAddress CreateIPAddress(byte* addr, short length) {
310                         byte* p = addr;
311                         long res = 0;
312                         for(int i = 0, j = length - 1; i < length; ++i, --j) {
313                                 res += *p << j * 8;
314                                 ++p;
315                         }
316                         if (res > uint.MaxValue)
317                                 return new IPAddress(IPAddress.NetworkToHostOrder(res));
318                         else
319                                 return new IPAddress(IPAddress.NetworkToHostOrder((int)res));
320                 }
321         }
322 }
323