Merge pull request #3563 from lewurm/interpreter
[mono.git] / mcs / class / System / System.Net.NetworkInformation / IPInterfaceProperties.cs
1 //
2 // System.Net.NetworkInformation.IPInterfaceProperties
3 //
4 // Authors:
5 //      Gonzalo Paniagua Javier (gonzalo@novell.com)
6 //      Atsushi Enomoto (atsushi@ximian.com)
7 //
8 // Copyright (c) 2006-2007 Novell, Inc. (http://www.novell.com)
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 // 
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 // 
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29 using System.Collections.Generic;
30 using System.Globalization;
31 using System.IO;
32 using System.Net.Sockets;
33 using System.Runtime.CompilerServices;
34 using System.Text.RegularExpressions;
35 using System.Runtime.InteropServices;
36
37 namespace System.Net.NetworkInformation {
38         abstract class UnixIPInterfaceProperties : IPInterfaceProperties
39         {
40                 protected IPv4InterfaceProperties ipv4iface_properties;
41                 protected UnixNetworkInterface iface;
42                 List <IPAddress> addresses;
43                 IPAddressCollection dns_servers;
44                 string dns_suffix;
45                 DateTime last_parse;
46                 
47                 public UnixIPInterfaceProperties (UnixNetworkInterface iface, List <IPAddress> addresses)
48                 {
49                         this.iface = iface;
50                         this.addresses = addresses;
51                 }
52
53                 public override IPv6InterfaceProperties GetIPv6Properties ()
54                 {
55                         throw new NotImplementedException ();
56                 }
57 #if MONODROID
58                 [DllImport ("__Internal")]
59                 static extern int _monodroid_get_dns_servers (out IntPtr dns_servers_array);
60
61                 void GetDNSServersFromOS ()
62                 {
63                         IntPtr dsa;
64                         int len = _monodroid_get_dns_servers (out dsa);
65                         if (len <= 0)
66                                 return;
67
68                         var servers = new IntPtr [len];
69                         Marshal.Copy (dsa, servers, 0, len);
70
71                         dns_servers = new IPAddressCollection ();
72                         foreach (IntPtr s in servers) {
73                                 string server_ip = Marshal.PtrToStringAnsi (s);
74                                 Marshal.FreeHGlobal (s);
75
76                                 IPAddress addr;
77                                 if (!IPAddress.TryParse (server_ip, out addr))
78                                         continue;
79                                 dns_servers.InternalAdd (addr);
80                         }
81                         Marshal.FreeHGlobal (dsa);
82                 }
83 #else
84                 static Regex ns = new Regex (@"\s*nameserver\s+(?<address>.*)");
85                 static Regex search = new Regex (@"\s*search\s+(?<domain>.*)");
86                 void ParseResolvConf ()
87                 {
88                         try {
89                                 DateTime wt = File.GetLastWriteTime ("/etc/resolv.conf");
90                                 if (wt <= last_parse)
91                                         return;
92
93                                 last_parse = wt;
94                                 dns_suffix = "";
95                                 dns_servers = new IPAddressCollection ();
96                                 using (StreamReader reader = new StreamReader ("/etc/resolv.conf")) {
97                                         string str;
98                                         string line;
99                                         while ((line = reader.ReadLine ()) != null) {
100                                                 line = line.Trim ();
101                                                 if (line.Length == 0 || line [0] == '#')
102                                                         continue;
103                                                 Match match = ns.Match (line);
104                                                 if (match.Success) {
105                                                         try {
106                                                                 str = match.Groups ["address"].Value;
107                                                                 str = str.Trim ();
108                                                                 dns_servers.InternalAdd (IPAddress.Parse (str));
109                                                         } catch {
110                                                         }
111                                                 } else {
112                                                         match = search.Match (line);
113                                                         if (match.Success) {
114                                                                 str = match.Groups ["domain"].Value;
115                                                                 string [] parts = str.Split (',');
116                                                                 dns_suffix = parts [0].Trim ();
117                                                         }
118                                                 }
119                                         }
120                                 }
121                         } catch {
122                         }
123                 }
124 #endif
125                 public override IPAddressInformationCollection AnycastAddresses {
126                         get {
127                                 var c = new IPAddressInformationCollection ();
128                                 foreach (IPAddress address in addresses) {
129                                         c.InternalAdd (new SystemIPAddressInformation (address, false, false));
130                                 }
131                                 return c;
132                         }
133                 }
134
135                 [MonoTODO ("Always returns an empty collection.")]
136                 public override IPAddressCollection DhcpServerAddresses {
137                         get {
138                                 // There are lots of different DHCP clients
139                                 // that all store their configuration differently.
140                                 // I'm not sure what to do here.
141                                 IPAddressCollection coll = new IPAddressCollection ();
142                                 return coll;
143                         }
144                 }
145
146                 public override IPAddressCollection DnsAddresses {
147                         get {
148 #if MONODROID
149                                 GetDNSServersFromOS ();
150 #else
151                                 ParseResolvConf ();
152 #endif
153                                 return dns_servers;
154                         }
155                 }
156
157                 public override string DnsSuffix {
158                         get {
159 #if MONODROID
160                                 return String.Empty;
161 #else
162                                 ParseResolvConf ();
163                                 return dns_suffix;
164 #endif
165                         }
166                 }
167
168                 [MonoTODO ("Always returns true")]
169                 public override bool IsDnsEnabled {
170                         get {
171                                 return true;
172                         }
173                 }
174
175                 [MonoTODO ("Always returns false")]
176                 public override bool IsDynamicDnsEnabled {
177                         get {
178                                 return false;
179                         }
180                 }
181
182                 public override MulticastIPAddressInformationCollection MulticastAddresses {
183                         get {
184                                 var multicastAddresses = new MulticastIPAddressInformationCollection ();
185                                 foreach (IPAddress address in addresses) {
186                                         byte[] addressBytes = address.GetAddressBytes ();
187                                         if (addressBytes[0] >= 224 && addressBytes[0] <= 239) {
188                                                 multicastAddresses.InternalAdd (new SystemMulticastIPAddressInformation (new SystemIPAddressInformation (address, true, false)));
189                                         }
190                                 }
191                                 return multicastAddresses;
192                         }
193                 }
194
195                 public override UnicastIPAddressInformationCollection UnicastAddresses {
196                         get {
197                                 var unicastAddresses = new UnicastIPAddressInformationCollection ();
198                                 foreach (IPAddress address in addresses) {
199                                         switch (address.AddressFamily) {
200                                                 case AddressFamily.InterNetwork:
201                                                         byte top = address.GetAddressBytes () [0];
202                                                         if (top >= 224 && top <= 239)
203                                                                 continue;
204                                                         unicastAddresses.InternalAdd (new LinuxUnicastIPAddressInformation (address));
205                                                         break;
206
207                                                 case AddressFamily.InterNetworkV6:
208                                                         if (address.IsIPv6Multicast)
209                                                                 continue;
210                                                         unicastAddresses.InternalAdd (new LinuxUnicastIPAddressInformation (address));
211                                                         break;
212                                         }
213                                 }
214                                 return unicastAddresses;
215                         }
216                 }
217
218                 [MonoTODO ("Always returns an empty collection.")]
219                 public override IPAddressCollection WinsServersAddresses {
220                         get {
221                                 // I do SUPPOSE we could scrape /etc/samba/smb.conf, but.. yeesh.
222                                 return new IPAddressCollection ();
223                         }
224                 }
225         }
226
227         class LinuxIPInterfaceProperties : UnixIPInterfaceProperties
228         {
229                 public LinuxIPInterfaceProperties (LinuxNetworkInterface iface, List <IPAddress> addresses)
230                         : base (iface, addresses)
231                 {
232                 }
233
234                 public override IPv4InterfaceProperties GetIPv4Properties ()
235                 {
236                         if (ipv4iface_properties == null)
237                                 ipv4iface_properties = new LinuxIPv4InterfaceProperties (iface as LinuxNetworkInterface);
238                         
239                         return ipv4iface_properties;
240                 }
241
242                 IPAddressCollection ParseRouteInfo (string iface)
243                 {
244                         var col = new IPAddressCollection ();
245                         try {
246                                 using (StreamReader reader = new StreamReader ("/proc/net/route")) {
247                                         string line;
248                                         reader.ReadLine (); // Ignore first line
249                                         while ((line = reader.ReadLine ()) != null) {
250                                                 line = line.Trim ();
251                                                 if (line.Length == 0)
252                                                         continue;
253
254                                                 string [] parts = line.Split ('\t');
255                                                 if (parts.Length < 3)
256                                                         continue;
257                                                 string gw_address = parts [2].Trim ();
258                                                 byte [] ipbytes = new byte [4];
259                                                 if (gw_address.Length == 8 && iface.Equals (parts [0], StringComparison.OrdinalIgnoreCase)) {
260                                                         for (int i = 0; i < 4; i++) {
261                                                                 if (!Byte.TryParse (gw_address.Substring (i * 2, 2), NumberStyles.HexNumber, null, out ipbytes [3 - i]))
262                                                                         continue;
263                                                         }
264                                                         IPAddress ip = new IPAddress (ipbytes);
265                                                         if (!ip.Equals (IPAddress.Any) && !col.Contains (ip))
266                                                                 col.InternalAdd (ip);
267                                                 }
268                                         }
269                                 }
270                         } catch {
271                         }
272
273                         return col;
274                 }
275
276                 public override GatewayIPAddressInformationCollection GatewayAddresses {
277                         get {
278                                 return SystemGatewayIPAddressInformation.ToGatewayIpAddressInformationCollection (ParseRouteInfo (this.iface.Name.ToString()));
279                         }
280                 }
281         }
282
283         class MacOsIPInterfaceProperties : UnixIPInterfaceProperties
284         {
285                 public MacOsIPInterfaceProperties (MacOsNetworkInterface iface, List <IPAddress> addresses)
286                         : base (iface, addresses)
287                 {
288                 }
289
290                 public override IPv4InterfaceProperties GetIPv4Properties ()
291                 {
292                         if (ipv4iface_properties == null)
293                                 ipv4iface_properties = new MacOsIPv4InterfaceProperties (iface as MacOsNetworkInterface);
294                         
295                         return ipv4iface_properties;
296                 }
297
298                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
299                 private extern static bool ParseRouteInfo_internal(string iface, out string[] gw_addr_list);
300
301                 public override GatewayIPAddressInformationCollection GatewayAddresses {
302                         get {
303                                 var gateways = new IPAddressCollection ();
304                                 string[] gw_addrlist;
305                                 if (!ParseRouteInfo_internal (this.iface.Name.ToString(), out gw_addrlist))
306                                         return new GatewayIPAddressInformationCollection ();
307
308                                 for(int i=0; i<gw_addrlist.Length; i++) {
309                                         try {
310                                                 IPAddress ip = IPAddress.Parse(gw_addrlist[i]);
311                                                 if (!ip.Equals (IPAddress.Any) && !gateways.Contains (ip))
312                                                         gateways.InternalAdd (ip);
313                                         } catch (ArgumentNullException) {
314                                                 /* Ignore this, as the
315                                                  * internal call might have
316                                                  * left some blank entries at
317                                                  * the end of the array
318                                                  */
319                                         }
320                                 }
321
322                                 return SystemGatewayIPAddressInformation.ToGatewayIpAddressInformationCollection (gateways);
323                         }
324                 }
325         }
326
327 #if !MOBILE
328         class Win32IPInterfaceProperties2 : IPInterfaceProperties
329         {
330                 readonly Win32_IP_ADAPTER_ADDRESSES addr;
331                 readonly Win32_MIB_IFROW mib4, mib6;
332
333                 public Win32IPInterfaceProperties2 (Win32_IP_ADAPTER_ADDRESSES addr, Win32_MIB_IFROW mib4, Win32_MIB_IFROW mib6)
334                 {
335                         this.addr = addr;
336                         this.mib4 = mib4;
337                         this.mib6 = mib6;
338                 }
339
340                 public override IPv4InterfaceProperties GetIPv4Properties ()
341                 {
342                         Win32_IP_ADAPTER_INFO v4info = Win32NetworkInterface2.GetAdapterInfoByIndex (mib4.Index);
343                         return v4info != null ? new Win32IPv4InterfaceProperties (v4info, mib4) : null;
344                 }
345
346                 public override IPv6InterfaceProperties GetIPv6Properties ()
347                 {
348                         Win32_IP_ADAPTER_INFO v6info = Win32NetworkInterface2.GetAdapterInfoByIndex (mib6.Index);
349                         return v6info != null ? new Win32IPv6InterfaceProperties (mib6) : null;
350                 }
351
352                 public override IPAddressInformationCollection AnycastAddresses {
353                         get { return Win32FromAnycast (addr.FirstAnycastAddress); }
354                 }
355
356                 static IPAddressInformationCollection Win32FromAnycast (IntPtr ptr)
357                 {
358                         var c = new IPAddressInformationCollection ();
359                         Win32_IP_ADAPTER_ANYCAST_ADDRESS a;
360                         for (IntPtr p = ptr; p != IntPtr.Zero; p = a.Next) {
361                                 a = (Win32_IP_ADAPTER_ANYCAST_ADDRESS) Marshal.PtrToStructure (p, typeof (Win32_IP_ADAPTER_ANYCAST_ADDRESS));
362                                 c.InternalAdd (new SystemIPAddressInformation (
363                                        a.Address.GetIPAddress (),
364                                        a.LengthFlags.IsDnsEligible,
365                                        a.LengthFlags.IsTransient));
366                         }
367                         return c;
368                 }
369
370                 public override IPAddressCollection DhcpServerAddresses {
371                         get {
372                                 Win32_IP_ADAPTER_INFO v4info = Win32NetworkInterface2.GetAdapterInfoByIndex (mib4.Index);
373                                 // FIXME: should ipv6 DhcpServer be considered?
374                                 return v4info != null ? new Win32IPAddressCollection (v4info.DhcpServer) : Win32IPAddressCollection.Empty;
375                         }
376                 }
377
378                 public override IPAddressCollection DnsAddresses {
379                         get { return Win32IPAddressCollection.FromDnsServer (addr.FirstDnsServerAddress); }
380                 }
381
382                 public override string DnsSuffix {
383                         get { return addr.DnsSuffix; }
384                 }
385
386                 public override GatewayIPAddressInformationCollection GatewayAddresses {
387                         get {
388                                 Win32_IP_ADAPTER_INFO v4info = Win32NetworkInterface2.GetAdapterInfoByIndex (mib4.Index);
389                                 // FIXME: should ipv6 DhcpServer be considered?
390
391                                 var col = new GatewayIPAddressInformationCollection ();
392                                 if (v4info != null) {
393                                         var a = v4info.GatewayList;
394                                         if (!String.IsNullOrEmpty (a.IpAddress)) {
395                                                 col.InternalAdd(new SystemGatewayIPAddressInformation(IPAddress.Parse (a.IpAddress)));
396                                                 AddSubsequently (a.Next, col);
397                                         }
398                                 }
399
400                                 return col;
401                         }
402                 }
403
404                 static void AddSubsequently (IntPtr head, GatewayIPAddressInformationCollection col)
405                 {
406                         Win32_IP_ADDR_STRING a;
407                         for (IntPtr p = head; p != IntPtr.Zero; p = a.Next) {
408                                 a = (Win32_IP_ADDR_STRING) Marshal.PtrToStructure (p, typeof (Win32_IP_ADDR_STRING));
409                                 col.InternalAdd (new SystemGatewayIPAddressInformation (IPAddress.Parse (a.IpAddress)));
410                         }
411                 }
412
413                 public override bool IsDnsEnabled {
414                         get { return Win32_FIXED_INFO.Instance.EnableDns != 0; }
415                 }
416
417                 public override bool IsDynamicDnsEnabled {
418                         get { return addr.DdnsEnabled; }
419                 }
420
421                 public override MulticastIPAddressInformationCollection MulticastAddresses {
422                         get { return Win32FromMulticast (addr.FirstMulticastAddress); }
423                 }
424
425                 static MulticastIPAddressInformationCollection Win32FromMulticast (IntPtr ptr)
426                 {
427                         var c = new MulticastIPAddressInformationCollection ();
428                         Win32_IP_ADAPTER_MULTICAST_ADDRESS a;
429                         for (IntPtr p = ptr; p != IntPtr.Zero; p = a.Next) {
430                                 a = (Win32_IP_ADAPTER_MULTICAST_ADDRESS) Marshal.PtrToStructure (p, typeof (Win32_IP_ADAPTER_MULTICAST_ADDRESS));
431                                 c.InternalAdd (new SystemMulticastIPAddressInformation (new SystemIPAddressInformation (
432                                        a.Address.GetIPAddress (),
433                                        a.LengthFlags.IsDnsEligible,
434                                        a.LengthFlags.IsTransient)));
435                         }
436                         return c;
437                 }
438
439                 public override UnicastIPAddressInformationCollection UnicastAddresses {
440                         get {
441                                 Win32_IP_ADAPTER_INFO ai = Win32NetworkInterface2.GetAdapterInfoByIndex (mib4.Index);
442                                 // FIXME: should ipv6 DhcpServer be considered?
443                                 return ai != null ? Win32FromUnicast (addr.FirstUnicastAddress) : new UnicastIPAddressInformationCollection ();
444                         }
445                 }
446
447                 static UnicastIPAddressInformationCollection Win32FromUnicast (IntPtr ptr)
448                 {
449                         UnicastIPAddressInformationCollection c = new UnicastIPAddressInformationCollection ();
450                         Win32_IP_ADAPTER_UNICAST_ADDRESS a;
451                         for (IntPtr p = ptr; p != IntPtr.Zero; p = a.Next) {
452                                 a = (Win32_IP_ADAPTER_UNICAST_ADDRESS) Marshal.PtrToStructure (p, typeof (Win32_IP_ADAPTER_UNICAST_ADDRESS));
453                                 c.InternalAdd (new Win32UnicastIPAddressInformation (a));
454                         }
455                         return c;
456                 }
457
458                 public override IPAddressCollection WinsServersAddresses {
459                         get {
460                                 Win32_IP_ADAPTER_INFO v4info = Win32NetworkInterface2.GetAdapterInfoByIndex (mib4.Index);
461                                 // FIXME: should ipv6 DhcpServer be considered?
462                                 return v4info != null ? new Win32IPAddressCollection (v4info.PrimaryWinsServer, v4info.SecondaryWinsServer) : Win32IPAddressCollection.Empty;
463                         }
464                 }
465
466         }
467 #endif
468
469 }
470
471