2009-01-26 Gonzalo Paniagua Javier <gonzalo@novell.com>
[mono.git] / mcs / class / System / System.Net / IPv6Address.cs
index 5e025d48d1d2d52c7b757e9bb9d2427a6c5504c5..7e2e4ddcc36946232bd5f33e2dd978f26f60c96d 100644 (file)
@@ -34,6 +34,7 @@
 \r
 \r
 using System;\r
+using System.Globalization;\r
 using System.Net.Sockets;\r
 using System.Runtime.InteropServices;\r
 using System.Text;\r
@@ -67,145 +68,209 @@ namespace System.Net {
                        if (prefixLength < 0 || prefixLength > 128)\r
                                throw new ArgumentException ("prefixLength");\r
                        this.prefixLength = prefixLength;\r
-               }       \r
+               }\r
        \r
                public IPv6Address (ushort [] addr, int prefixLength, int scopeId) : this (addr, prefixLength)\r
                {\r
-            this.scopeId = scopeId;\r
-               }               \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
+                       IPv6Address result;\r
+                       if (TryParse (ipString, out result))\r
+                               return result;\r
+                       throw new FormatException ("Not a valid IPv6 address");\r
+               }\r
+\r
+               static int Fill (ushort [] addr, string ipString)\r
+               {\r
+                       int p = 0;\r
+                       int slot = 0;\r
+\r
+                       if (ipString.Length == 0)\r
+                               return 0;\r
+                       \r
+                       // Catch double uses of ::\r
+                       if (ipString.IndexOf ("::") != -1)\r
+                               return -1;\r
+\r
+                       for (int i = 0; i < ipString.Length; i++){\r
+                               char c = ipString [i];\r
+                               int n;\r
+\r
+                               if (c == ':'){\r
+                                       // Trailing : is not allowed.\r
+                                       if (i == ipString.Length-1)\r
+                                               return -1;\r
+                                       \r
+                                       if (slot == 8)\r
+                                               return -1;\r
+                                       \r
+                                       addr [slot++] = (ushort) p;\r
+                                       p = 0;\r
+                                       continue;\r
+                               } if ('0' <= c && c <= '9')\r
+                                       n = (int) (c - '0');\r
+                               else if ('a' <= c && c <= 'f')\r
+                                       n = (int) (c - 'a' + 10);\r
+                               else if ('A' <= c && c <= 'F')\r
+                                       n = (int) (c - 'A' + 10);\r
+                               else \r
+                                       return -1;\r
+                               p = (p << 4) + n;\r
+                               if (p > UInt16.MaxValue)\r
+                                       return -1;\r
+                       }\r
+\r
+                       if (slot == 8)\r
+                               return -1;\r
+                       \r
+                       addr [slot++] = (ushort) p;\r
+\r
+                       return slot;\r
+               }\r
+\r
+               static bool TryParse (string prefix, out int res)\r
+               {\r
+#if NET_2_0\r
+                       return Int32.TryParse (prefix, NumberStyles.Integer, CultureInfo.InvariantCulture, out res);\r
+#else\r
+                       try {\r
+                               res = Int32.Parse (prefix, NumberStyles.Integer, CultureInfo.InvariantCulture);\r
+                       } catch (Exception) {\r
+                               res = -1;\r
+                               return false;\r
+                       }\r
+\r
+                       return true;\r
+#endif\r
+               }\r
+               \r
+               public static bool TryParse (string ipString, out IPv6Address result)\r
+               {\r
+                       result = null;\r
+                       if (ipString == null)\r
+                               return false;\r
+\r
                        if (ipString.Length > 2 && \r
                            ipString [0] == '[' && \r
                            ipString [ipString.Length - 1] == ']')\r
-                               ipString = ipString.Substring (1, ipString.Length - 2);                         \r
+                               ipString = ipString.Substring (1, ipString.Length - 2);\r
 \r
                        if (ipString.Length  < 2)\r
-                               throw new FormatException ("Not a valid IPv6 address");\r
+                               return false;\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
+                               if (!TryParse (prefix , out prefixLen))\r
                                        prefixLen = -1;\r
-                               }\r
                                if (prefixLen < 0 || prefixLen > 128)\r
-                                       throw new FormatException ("Not a valid prefix length");\r
+                                       return false;\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
+                                       if (!TryParse (prefix, out scopeId))\r
                                                scopeId = 0;\r
-                                       }\r
                                        ipString = ipString.Substring (0, pos);\r
                                }                       \r
                        }\r
-                       \r
-                       ushort [] addr = new ushort [8];                        \r
-                       \r
+\r
+                       //\r
+                       // At this point the prefix/suffixes have been removed\r
+                       // and we only have to deal with the ipv4 or ipv6 addressed\r
+                       //\r
+                       ushort [] addr = new ushort [8];\r
+\r
+                       //\r
+                       // Is there an ipv4 address at the end?\r
+                       //\r
                        bool ipv4 = false;\r
-                       int pos2 = ipString.LastIndexOf (":");\r
+                       int pos2 = ipString.LastIndexOf (':');\r
                        if (pos2 == -1)\r
-                               throw new FormatException ("Not a valid IPv6 address");\r
+                               return false;\r
+\r
+                       int slots = 0;\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
+                                       IPAddress ip;\r
+                                       \r
+                                       if (!IPAddress.TryParse (ipv4Str, out ip))\r
+                                               return false;\r
+                                       \r
+                                       long a = ip.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 (pos2 > 0 && ipString [pos2 - 1] == ':') \r
+                                               ipString = ipString.Substring (0, pos2 + 1);\r
+                                       else\r
+                                               ipString = ipString.Substring (0, pos2);\r
+                                       ipv4 = true;\r
+                                       slots = 2;\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
-                       bool ipv6 = false;\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
-                                       if (p != 0 || (i == 5 && p != 0xffff))\r
-                                               ipv6 = true;\r
+\r
+                       //\r
+                       // Only an ipv6 block remains, either:\r
+                       // "hexnumbers::hexnumbers", "hexnumbers::" or "hexnumbers"\r
+                       //\r
+                       int c = ipString.IndexOf ("::");\r
+                       if (c != -1){\r
+                               int right_slots = Fill (addr, ipString.Substring (c+2));\r
+                               if (right_slots == -1){\r
+                                       return false;\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
+                               if (right_slots + slots > 8){\r
+                                       return false;\r
                                }\r
-                               for (; i >= piecedouble; i--) {\r
-                                       addr [i] = 0;\r
+\r
+                               int d = 8-slots-right_slots;\r
+                               for (int i = right_slots; i > 0; i--){\r
+                                       addr [i+d-1] = addr [i-1];\r
+                                       addr [i-1] = 0;\r
                                }\r
-                       } else if (len != (ipv4 ? 6 : 8)) \r
-                               throw new FormatException ("Not a valid IPv6 address");\r
+                               \r
+                               int left_slots = Fill (addr, ipString.Substring (0, c));\r
+                               if (left_slots == -1)\r
+                                       return false;\r
 \r
+                               if (left_slots + right_slots + slots > 7)\r
+                                       return false;\r
+                       } else {\r
+                               if (Fill (addr, ipString) != 8-slots)\r
+                                       return false;\r
+                       }\r
+\r
+                       // Now check the results in the ipv6-address range only\r
+                       bool ipv6 = false;\r
+                       for (int i = 0; i < slots; i++){\r
+                               if (addr [i] != 0 || i == 5 && addr [i] != 0xffff)\r
+                                       ipv6 = true;\r
+                       }\r
+                       \r
                        // check IPv4 validity\r
                        if (ipv4 && !ipv6) {\r
                                for (int i = 0; i < 5; i++) {\r
                                        if (addr [i] != 0)\r
-                                               throw new FormatException ("Not a valid IPv6 address");\r
+                                               return false;\r
                                }\r
 \r
                                if (addr [5] != 0 && addr [5] != 0xffff)\r
-                                       throw new FormatException ("Not a valid IPv6 address");\r
+                                       return false;\r
                        }\r
 \r
-                       return new IPv6Address (addr, prefixLen, scopeId);\r
+                       result = new IPv6Address (addr, prefixLen, scopeId);\r
+                       return true;\r
                }\r
 \r
                public ushort [] Address {\r
@@ -344,6 +409,8 @@ namespace System.Net {
                                        if (i < 7) s.Append (':');\r
                                }\r
                        }\r
+                       if (scopeId != 0)\r
+                               s.Append ('%').Append (scopeId);\r
                        return s.ToString ();\r
                }\r
 \r
@@ -370,14 +437,14 @@ namespace System.Net {
                                for (int i = 0; i < 8; i++) \r
                                        if (this.address [i] != ipv6.address [i])\r
                                                return false;\r
-                               return true;                            \r
-                       } \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
+                                               return false;\r
 \r
                                if (address [5] != 0 && address [5] != 0xffff)\r
                                        return false;\r
@@ -385,8 +452,8 @@ namespace System.Net {
                                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 false;\r
+\r
                                return true;\r
                        }\r
                        \r