Merge pull request #4680 from lambdageek/bug-49721-take2
[mono.git] / mcs / class / System / System.Net / IPv6Address.cs
index 0b7d8c51b599553fb76ca7b9c3191d72167820e0..3592427d41e04b17fea2d4af5700ea4a14a1bd1c 100644 (file)
 //\r
-// System.Net.IPv6Address.cs\r
+// System.Net.IPv6AddressFormatter.cs\r
 //\r
 // Author:\r
 //   Lawrence Pit (loz@cable.a2000.nl)\r
 //\r
-// Note I: This class is not defined in the specs of .Net\r
+// Permission is hereby granted, free of charge, to any person obtaining\r
+// a copy of this software and associated documentation files (the\r
+// "Software"), to deal in the Software without restriction, including\r
+// without limitation the rights to use, copy, modify, merge, publish,\r
+// distribute, sublicense, and/or sell copies of the Software, and to\r
+// permit persons to whom the Software is furnished to do so, subject to\r
+// the following conditions:\r
+// \r
+// The above copyright notice and this permission notice shall be\r
+// included in all copies or substantial portions of the Software.\r
+// \r
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,\r
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF\r
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND\r
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE\r
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION\r
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION\r
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\r
 //\r
-// Note II : The name of this class is perhaps unfortunate as it turns\r
-//           out that in ms.net there's an internal class called\r
-//           IPv6Address in namespace System.\r
-//\r
-
-//
-// Permission is hereby granted, free of charge, to any person obtaining
-// a copy of this software and associated documentation files (the
-// "Software"), to deal in the Software without restriction, including
-// without limitation the rights to use, copy, modify, merge, publish,
-// distribute, sublicense, and/or sell copies of the Software, and to
-// permit persons to whom the Software is furnished to do so, subject to
-// the following conditions:
-// 
-// The above copyright notice and this permission notice shall be
-// included in all copies or substantial portions of the Software.
-// 
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
-// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
-// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
-// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-//
-\r
 \r
-using System;\r
-using System.Net.Sockets;\r
-using System.Runtime.InteropServices;\r
+using System.Globalization;\r
 using System.Text;\r
 \r
 namespace System.Net {\r
 \r
-       /// <remarks>\r
-       ///   Encapsulates an IPv6 Address.\r
-       ///   See RFC 2373 for more info on IPv6 addresses.\r
-       /// </remarks>\r
-       [Serializable]\r
-       internal class IPv6Address {\r
-               private ushort [] address;\r
-               private int prefixLength;\r
-               private long scopeId = 0;\r
-\r
-               public static readonly IPv6Address Loopback = IPv6Address.Parse ("::1");\r
-               public static readonly IPv6Address Unspecified = IPv6Address.Parse ("::");\r
+       struct IPv6AddressFormatter\r
+       {\r
+               ushort [] address;\r
+               long scopeId;\r
 \r
-               public IPv6Address (ushort [] addr)\r
+               public IPv6AddressFormatter (ushort[] addr, long scopeId)\r
                {\r
-                       if (addr == null)\r
-                               throw new ArgumentNullException ("addr");       \r
-                       if (addr.Length != 8)   \r
-                               throw new ArgumentException ("addr");\r
-                       address = addr;                 \r
+                       this.address = addr;\r
+                       this.scopeId = scopeId;\r
                }\r
-               \r
-               public IPv6Address (ushort [] addr, int prefixLength) : this (addr)\r
-               {\r
-                       if (prefixLength < 0 || prefixLength > 128)\r
-                               throw new ArgumentException ("prefixLength");\r
-                       this.prefixLength = prefixLength;\r
-               }       \r
-       \r
-               public IPv6Address (ushort [] addr, int prefixLength, int scopeId) : this (addr, prefixLength)\r
-               {\r
-            this.scopeId = scopeId;\r
-               }               \r
-               \r
-               public static IPv6Address Parse (string ipString)\r
-               {\r
-                       if (ipString == null)\r
-                               throw new ArgumentNullException ("ipString");\r
-\r
-                       if (ipString.Length > 2 && \r
-                           ipString [0] == '[' && \r
-                           ipString [ipString.Length - 1] == ']')\r
-                               ipString = ipString.Substring (1, ipString.Length - 2);                         \r
-\r
-                       if (ipString.Length  < 2)\r
-                               throw new FormatException ("Not a valid IPv6 address");\r
 \r
-                       int prefixLen = 0;\r
-                       int scopeId = 0;\r
-                       int pos = ipString.LastIndexOf ('/');\r
-                       if (pos != -1) {\r
-                               string prefix = ipString.Substring (pos + 1);\r
-                               try {\r
-                                       prefixLen = Int32.Parse (prefix);\r
-                               } catch (Exception) {\r
-                                       prefixLen = -1;\r
-                               }\r
-                               if (prefixLen < 0 || prefixLen > 128)\r
-                                       throw new FormatException ("Not a valid prefix length");\r
-                               ipString = ipString.Substring (0, pos);\r
-                       } else {\r
-                               pos = ipString.LastIndexOf ('%');\r
-                               if (pos != -1) {\r
-                                       string prefix = ipString.Substring (pos + 1);\r
-                                       try  {\r
-                                               scopeId = Int32.Parse (prefix);\r
-                                       } \r
-                                       catch (Exception) {\r
-                                               scopeId = 0;\r
-                                       }\r
-                                       ipString = ipString.Substring (0, pos);\r
-                               }                       \r
-                       }\r
-                       \r
-                       ushort [] addr = new ushort [8];                        \r
-                       \r
-                       bool ipv4 = false;\r
-                       int pos2 = ipString.LastIndexOf (":");\r
-                       if (pos2 == -1)\r
-                               throw new FormatException ("Not a valid IPv6 address");\r
-                       if (pos2 < (ipString.Length - 1)) {\r
-                               string ipv4Str = ipString.Substring (pos2 + 1);\r
-                               if (ipv4Str.IndexOf ('.') != -1) {\r
-                                       try {\r
-                                               long a = IPAddress.Parse (ipv4Str).InternalIPv4Address;\r
-                                               addr [6] = (ushort) (((int) (a & 0xff) << 8) + ((int) ((a >> 8) & 0xff)));\r
-                                               addr [7] = (ushort) (((int) ((a >> 16) & 0xff) << 8) + ((int) ((a >> 24) & 0xff)));\r
-                                               if (ipString [pos2 - 1] == ':') \r
-                                                       ipString = ipString.Substring (0, pos2 + 1);\r
-                                               else\r
-                                                       ipString = ipString.Substring (0, pos2);\r
-                                               ipv4 = true;\r
-                                       } catch (Exception) {\r
-                                               throw new FormatException ("Not a valid IPv6 address");         \r
-                                       }\r
-                               }\r
-                       }       \r
-                       \r
-                       int origLen = ipString.Length;\r
-                       if (origLen < 2)\r
-                               throw new FormatException ("Not a valid IPv6 address");                 \r
-                       ipString = ipString.Replace ("::", ":!:");\r
-                       int len = ipString.Length;\r
-                       if ((len - origLen) > 1) \r
-                               throw new FormatException ("Not a valid IPv6 address");\r
-                       \r
-                       if (ipString [1] == '!') \r
-                               ipString = ipString.Remove (0, 1);\r
-                       if (ipString [len - 2] == '!')\r
-                               ipString = ipString.Remove (len - 1, 1);\r
-                       if ((ipString.Length > 2) && \r
-                           ((ipString [0] == ':') || (ipString [ipString.Length - 1] == ':'))) \r
-                               throw new FormatException ("Not a valid IPv6 address");\r
-                               \r
-                       string [] pieces = ipString.Split (new char [] {':'});\r
-                       len = pieces.Length;\r
-                       if (len > (ipv4 ? 6 : 8)) \r
-                               throw new FormatException ("Not a valid IPv6 address");\r
-                       int piecedouble = -1;\r
-                       for (int i = 0; i < len; i++) {\r
-                               string piece = pieces [i];\r
-                               if (piece == "!")\r
-                                       piecedouble = i;\r
-                               else {\r
-                                       int plen = piece.Length;\r
-                                       if (plen > 4)\r
-                                               throw new FormatException ("Not a valid IPv6 address");\r
-                                       int p = 0;\r
-                                       for (int j = 0; j < plen; j++) \r
-                                               try {\r
-                                                       p = (p << 4) + Uri.FromHex (piece [j]);\r
-                                               } catch (ArgumentException) {\r
-                                                       throw new FormatException ("Not a valid IPv6 address");\r
-                                               }\r
-                                       addr [i] = (ushort) p;\r
-                               }\r
-                       }\r
-\r
-                       //expand the :: token\r
-                       if (piecedouble != -1) {\r
-                               int totallen = (ipv4 ? 5 : 7);\r
-                               int i = totallen;\r
-                               for (i = totallen; i >= (totallen - (len - piecedouble - 1)); i--) {\r
-                                       addr [i] = addr [(len - 1) + i - totallen];\r
-                               }\r
-                               for (; i >= piecedouble; i--) {\r
-                                       addr [i] = 0;\r
-                               }\r
-                       } else if (len != (ipv4 ? 6 : 8)) \r
-                               throw new FormatException ("Not a valid IPv6 address");\r
-\r
-                       // check IPv4 validity\r
-                       if (ipv4) {\r
-                               for (int i = 0; i < 5; i++) \r
-                                       if (addr [i] != 0)\r
-                                               throw new FormatException ("Not a valid IPv6 address");\r
-                               if (addr [5] != 0 && addr [5] != 0xffff)\r
-                                       throw new FormatException ("Not a valid IPv6 address");\r
-                       }\r
-                       \r
-                       return new IPv6Address (addr, prefixLen, scopeId);\r
-               }\r
-               \r
-               public ushort [] Address {\r
-                       get { return address; }\r
-               }\r
-\r
-               public int PrefixLength {\r
-                       get { return this.prefixLength; }\r
-               }\r
-               \r
-               public long ScopeId {\r
-                       get {\r
-                               return scopeId;\r
-                       }\r
-                       set {\r
-                               scopeId = value;\r
-                       }\r
+               static ushort SwapUShort (ushort number)\r
+               {\r
+                       return (ushort) ( ((number >> 8) & 0xFF) + ((number << 8) & 0xFF00) );\r
                }\r
 \r
-               public ushort this [int index] {\r
-                       get { return address [index]; }\r
-               }               \r
-\r
-               public AddressFamily AddressFamily {\r
-                       get { return AddressFamily.InterNetworkV6; }\r
-               }\r
-               \r
-               /// <summary>\r
-               ///   Used to tell whether the given address is the loopback address.\r
-               /// </summary>\r
-               public static bool IsLoopback (IPv6Address addr)\r
+               // Convert the address into a format expected by the IPAddress (long) ctor\r
+               // This needs to be unsigned to satisfy the '> 1' test in IsIPv4Compatible()\r
+               uint AsIPv4Int ()\r
                {\r
-                       for (int i = 0; i < 4; i++)\r
-                               if (addr.address [i] != 0)\r
-                                       return false;\r
-                       if ((addr.address [5] != 0) && (addr.address [5] != 0xffff))\r
-                               return false;\r
-                       if ((addr.address [6] >> 8) == 0x7f)\r
-                               return true;\r
-                       return ((addr.address [5] == 0) && \r
-                               (addr.address [6] == 0) && \r
-                               (addr.address [7] == 1));\r
-               }\r
-               \r
-
-               private static ushort SwapUShort (ushort number)
-               {
-                       return (ushort) ( ((number >> 8) & 0xFF) + ((number << 8) & 0xFF00) );
-               }
-
-               // Convert the address into a format expected by the IPAddress (long) ctor
-               private int AsIPv4Int ()
-               {
-                       return (SwapUShort (address [7]) << 16) + SwapUShort (address [6]);
-               }                       
-
-               public bool IsIPv4Compatible ()\r
+                       return (uint)(SwapUShort (address [7]) << 16) + SwapUShort (address [6]);\r
+               }                       \r
+\r
+               bool IsIPv4Compatible ()\r
                {\r
                        for (int i = 0; i < 6; i++) \r
                                if (address [i] != 0)\r
                                        return false;\r
-                       return (AsIPv4Int () > 1);
+                       /* MS .net only seems to format the last 4\r
+                        * bytes as an IPv4 address if address[6] is\r
+                        * non-zero\r
+                        */\r
+                       if (address[6] == 0)\r
+                               return false;\r
+                       return (AsIPv4Int () > 1);\r
                }\r
                \r
-               public bool IsIPv4Mapped ()\r
+               bool IsIPv4Mapped ()\r
                {\r
                        for (int i = 0; i < 5; i++) \r
                                if (address [i] != 0)\r
                                        return false;\r
+                       /* MS .net only seems to format the last 4\r
+                        * bytes as an IPv4 address if address[6] is\r
+                        * non-zero\r
+                        */\r
+                       if (address[6] == 0)\r
+                               return false;\r
+                       \r
                        return address [5] == 0xffff;\r
                }\r
                \r
-               /// <summary>\r
-               ///   Overrides System.Object.ToString to return\r
-               ///   this object rendered in a canonicalized notation\r
-               /// </summary>\r
                public override string ToString ()\r
                {\r
                        StringBuilder s = new StringBuilder ();\r
@@ -289,101 +93,56 @@ namespace System.Net {
                                if(IsIPv4Mapped())\r
                                        s.Append("ffff:");\r
 \r
-                               s.Append(new IPAddress( AsIPv4Int ()).ToString ());
+                               s.Append(new IPAddress( AsIPv4Int ()).ToString ());\r
 \r
                                return s.ToString ();\r
                        }\r
-                       else\r
-                       {\r
-                               int bestChStart = -1; // Best chain start\r
-                               int bestChLen = 0; // Best chain length\r
-                               int currChLen = 0; // Current chain length\r
+                       \r
+                       int bestChStart = -1; // Best chain start\r
+                       int bestChLen = 0; // Best chain length\r
+                       int currChLen = 0; // Current chain length\r
 \r
-                               // Looks for the longest zero chain\r
-                               for (int i=0; i<8; i++)\r
+                       // Looks for the longest zero chain\r
+                       for (int i=0; i<8; i++)\r
+                       {\r
+                               if (address[i] != 0)\r
                                {\r
-                                       if (address[i] != 0)\r
+                                       if ((currChLen > bestChLen) \r
+                                               && (currChLen > 1))\r
                                        {\r
-                                               if ((currChLen > bestChLen) \r
-                                                       && (currChLen > 1))\r
-                                               {\r
-                                                       bestChLen = currChLen;\r
-                                                       bestChStart = i - currChLen;\r
-                                               }\r
-                                               currChLen = 0;\r
+                                               bestChLen = currChLen;\r
+                                               bestChStart = i - currChLen;\r
                                        }\r
-                                       else\r
-                                               currChLen++;\r
-                               }\r
-                               if ((currChLen > bestChLen) \r
-                                       && (currChLen > 1))\r
-                               {\r
-                                       bestChLen = currChLen;\r
-                                       bestChStart = 8 - currChLen;\r
+                                       currChLen = 0;\r
                                }\r
+                               else\r
+                                       currChLen++;\r
+                       }\r
+                       if ((currChLen > bestChLen) \r
+                               && (currChLen > 1))\r
+                       {\r
+                               bestChLen = currChLen;\r
+                               bestChStart = 8 - currChLen;\r
+                       }\r
 \r
-                               // makes the string\r
-                               if (bestChStart == 0)\r
-                                       s.Append(":");\r
-                               for (int i=0; i<8; i++)\r
+                       // makes the string\r
+                       if (bestChStart == 0)\r
+                               s.Append(":");\r
+                       for (int i=0; i<8; i++)\r
+                       {\r
+                               if (i == bestChStart)\r
                                {\r
-                                       if (i == bestChStart)\r
-                                       {\r
-                                               s.Append (":");\r
-                                               i += (bestChLen - 1);\r
-                                               continue;\r
-                                       }\r
-                                       s.AppendFormat("{0:x}", address [i]);\r
-                                       if (i < 7) s.Append (':');\r
+                                       s.Append (":");\r
+                                       i += (bestChLen - 1);\r
+                                       continue;\r
                                }\r
-                       }\r
-                       return s.ToString ();\r
-               }\r
-\r
-               /// <returns>\r
-               ///   Whether both objects are equal.\r
-               /// </returns>\r
-               public override bool Equals (object other)\r
-               {\r
-                       System.Net.IPv6Address ipv6 = other as System.Net.IPv6Address;\r
-                       if (ipv6 != null) {\r
-                               for (int i = 0; i < 8; i++) \r
-                                       if (this.address [i] != ipv6.address [i])\r
-                                               return false;\r
-                               return true;                            \r
-                       } \r
-                       \r
-                       System.Net.IPAddress ipv4 = other as System.Net.IPAddress;\r
-                       if (ipv4 != null) {\r
-                               for (int i = 0; i < 5; i++) \r
-                                       if (address [i] != 0)\r
-                                               return false;                   \r
-\r
-                               if (address [5] != 0 && address [5] != 0xffff)\r
-                                       return false;\r
-\r
-                               long a = ipv4.InternalIPv4Address;\r
-                               if (address [6] != (ushort) (((int) (a & 0xff) << 8) + ((int) ((a >> 8) & 0xff))) ||\r
-                                   address [7] != (ushort) (((int) ((a >> 16) & 0xff) << 8) + ((int) ((a >> 24) & 0xff))))\r
-                                       return false;\r
-                                       \r
-                               return true;\r
+                               s.AppendFormat("{0:x}", address [i]);\r
+                               if (i < 7) s.Append (':');\r
                        }\r
                        \r
-                       return false;\r
-               }\r
-\r
-               public override int GetHashCode ()\r
-               {\r
-                       return Hash (((((int) address [0]) << 16) + address [1]), \r
-                                               ((((int) address [2]) << 16) + address [3]),\r
-                                               ((((int) address [4]) << 16) + address [5]),\r
-                                               ((((int) address [6]) << 16) + address [7]));\r
-               }\r
-               \r
-               private static int Hash (int i, int j, int k, int l) \r
-               {\r
-                       return i ^ (j << 13 | j >> 19) ^ (k << 26 | k >> 6) ^ (l << 7 | l >> 25);\r
+                       if (scopeId != 0)\r
+                               s.Append ('%').Append (scopeId);\r
+                       return s.ToString ();\r
                }\r
        }\r
 }\r