From 2d0613516626960de5d7e3ea83b6386ce00dcb74 Mon Sep 17 00:00:00 2001
From: Adam Frisby
Date: Sat, 23 May 2009 05:09:10 +0000
Subject: * Adds new NetworkUtil class, contains methods for handling IP
 resolution when located on the same subnet. (Eg, can be used to avoid NAT
 Loopback requirements if fully utilized.) * Adds a few new network-related
 methods to Util.

---
 OpenSim/Framework/NetworkUtil.cs | 122 +++++++++++++++++++++++++++++++++++++++
 OpenSim/Framework/Util.cs        |  14 +++++
 2 files changed, 136 insertions(+)
 create mode 100644 OpenSim/Framework/NetworkUtil.cs

diff --git a/OpenSim/Framework/NetworkUtil.cs b/OpenSim/Framework/NetworkUtil.cs
new file mode 100644
index 0000000..1abf0d8
--- /dev/null
+++ b/OpenSim/Framework/NetworkUtil.cs
@@ -0,0 +1,122 @@
+using System.Collections.Generic;
+using System.Net.Sockets;
+using System.Net;
+using System.Net.NetworkInformation;
+
+namespace OpenSim.Framework
+{
+    /// <summary>
+    /// Handles NAT translation in a 'manner of speaking'
+    /// Allows you to return multiple different external
+    /// hostnames depending on the requestors network
+    /// 
+    /// This enables standard port forwarding techniques
+    /// to work correctly with OpenSim.
+    /// </summary>
+    static class NetworkUtil
+    {
+        // IPv4Address, Subnet
+        static readonly Dictionary<IPAddress,IPAddress> m_subnets = new Dictionary<IPAddress, IPAddress>();
+
+        private static IPAddress GetExternalIPFor(IPAddress destination, string defaultHostname)
+        {
+            // Adds IPv6 Support (Not that any of the major protocols supports it...)
+            if (destination.AddressFamily == AddressFamily.InterNetworkV6)
+            {
+                foreach (IPAddress host in Dns.GetHostAddresses(defaultHostname))
+                {
+                    if (host.AddressFamily == AddressFamily.InterNetworkV6)
+                        return host;
+                }
+            }
+
+            if(destination.AddressFamily != AddressFamily.InterNetwork)
+                return null;
+
+            // Check if we're accessing localhost.
+            foreach (IPAddress host in Dns.GetHostAddresses(Dns.GetHostName()))
+            {
+                if (host.Equals(destination))
+                    return destination;
+            }
+
+            // Check for same LAN segment
+            foreach (KeyValuePair<IPAddress, IPAddress> subnet in m_subnets)
+            {
+                byte[] subnetBytes = subnet.Value.GetAddressBytes();
+                byte[] localBytes = subnet.Key.GetAddressBytes();
+                byte[] destBytes = destination.GetAddressBytes();
+                
+                if(subnetBytes.Length != destBytes.Length || subnetBytes.Length != localBytes.Length)
+                    return null;
+
+                bool valid = true;
+
+                for(int i=0;i<subnetBytes.Length;i++)
+                {
+                    if ((localBytes[i] & subnetBytes[i]) != (destBytes[i] & subnetBytes[i]))
+                    {
+                        valid = false;
+                        break;
+                    }
+                }
+
+                if (valid)
+                    return subnet.Key;
+            }
+
+            // None found. Assume outside network.
+            return null;
+        }
+
+        static NetworkUtil()
+        {
+            foreach (NetworkInterface ni in NetworkInterface.GetAllNetworkInterfaces())
+            {
+                foreach (UnicastIPAddressInformation address in ni.GetIPProperties().UnicastAddresses)
+                {
+                    if (address.Address.AddressFamily == AddressFamily.InterNetwork)
+                    {
+                        m_subnets.Add(address.Address, address.IPv4Mask);
+                    }
+                }
+            }
+        }
+
+        public static IPAddress GetIPFor(IPEndPoint user, string defaultHostname)
+        {
+            // Try subnet matching
+            IPAddress rtn = GetExternalIPFor(user.Address, defaultHostname);
+            if (rtn != null)
+                return rtn;
+
+            // Otherwise use the old algorithm
+            IPAddress ia;
+
+            if (IPAddress.TryParse(defaultHostname, out ia))
+                return ia;
+
+            ia = null;
+
+            foreach (IPAddress Adr in Dns.GetHostAddresses(defaultHostname))
+            {
+                if (Adr.AddressFamily == AddressFamily.InterNetwork)
+                {
+                    ia = Adr;
+                    break;
+                }
+            }
+
+            return ia;
+        }
+
+        public static string GetHostFor(IPAddress user, string defaultHostname)
+        {
+            IPAddress rtn = GetExternalIPFor(user, defaultHostname);
+            if(rtn != null)
+                return rtn.ToString();
+
+            return defaultHostname;
+        }
+    }
+}
diff --git a/OpenSim/Framework/Util.cs b/OpenSim/Framework/Util.cs
index 35efa02..0128735 100644
--- a/OpenSim/Framework/Util.cs
+++ b/OpenSim/Framework/Util.cs
@@ -517,6 +517,20 @@ namespace OpenSim.Framework
             return null;
         }
 
+        public static Uri GetURI(string protocol, string hostname, int port, string path)
+        {
+            return new UriBuilder(protocol, hostname, port, path).Uri;
+        }
+
+        /// <summary>
+        /// Gets a list of all local system IP addresses
+        /// </summary>
+        /// <returns></returns>
+        public static IPAddress[] GetLocalHosts()
+        {
+            return Dns.GetHostAddresses(Dns.GetHostName());
+        }
+
         public static IPAddress GetLocalHost()
         {
             string dnsAddress = "localhost";
-- 
cgit v1.1