[system] Redirect of put requests was converted to get. Fixes #16670
[mono.git] / mcs / class / System / System.Net / IPv6Address.cs
index 0b7d8c51b599553fb76ca7b9c3191d72167820e0..26bb3497c123cee8781c0c4c2e3028d56e218d7d 100644 (file)
 //           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
+// 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
 \r
 \r
 using System;\r
+using System.Globalization;\r
 using System.Net.Sockets;\r
 using System.Runtime.InteropServices;\r
 using System.Text;\r
@@ -67,142 +68,200 @@ 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 ("::", StringComparison.Ordinal) != -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
+                       return Int32.TryParse (prefix, NumberStyles.Integer, CultureInfo.InvariantCulture, out res);\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
-                       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
+                       // Only an ipv6 block remains, either:\r
+                       // "hexnumbers::hexnumbers", "hexnumbers::" or "hexnumbers"\r
+                       //\r
+                       int c = ipString.IndexOf ("::", StringComparison.Ordinal);\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) {\r
-                               for (int i = 0; i < 5; i++) \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
+\r
+                       result = new IPv6Address (addr, prefixLen, scopeId);\r
+                       return true;\r
                }\r
-               \r
+\r
                public ushort [] Address {\r
                        get { return address; }\r
                }\r
@@ -227,42 +286,44 @@ namespace System.Net {
                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
+\r
                public static bool IsLoopback (IPv6Address addr)\r
                {\r
-                       for (int i = 0; i < 4; i++)\r
+                       if (addr.address [7] != 1)\r
+                               return false;\r
+\r
+                       int x = addr.address [6] >> 8;\r
+                       if (x != 0x7f && x != 0)\r
+                               return false;\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
+                       }\r
+\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
+                       return true;\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]);
-               }                       
-
+\r
+               private static ushort SwapUShort (ushort number)\r
+               {\r
+                       return (ushort) ( ((number >> 8) & 0xFF) + ((number << 8) & 0xFF00) );\r
+               }\r
+\r
+               // Convert the address into a format expected by the IPAddress (long) ctor\r
+               private int AsIPv4Int ()\r
+               {\r
+                       return (SwapUShort (address [7]) << 16) + SwapUShort (address [6]);\r
+               }                       \r
+\r
                public bool IsIPv4Compatible ()\r
                {\r
                        for (int i = 0; i < 6; i++) \r
                                if (address [i] != 0)\r
                                        return false;\r
-                       return (AsIPv4Int () > 1);
+                       return (AsIPv4Int () > 1);\r
                }\r
                \r
                public bool IsIPv4Mapped ()\r
@@ -289,7 +350,7 @@ 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
@@ -337,9 +398,24 @@ 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
+               public string ToString (bool fullLength)\r
+               {\r
+                       if (!fullLength)\r
+                               return ToString ();\r
+\r
+                       StringBuilder sb = new StringBuilder ();\r
+                       for (int i=0; i < address.Length - 1; i++) {\r
+                               sb.AppendFormat ("{0:X4}:", address [i]);\r
+                       }\r
+                       sb.AppendFormat ("{0:X4}", address [address.Length - 1]);\r
+                       return sb.ToString ();\r
+               }\r
+\r
                /// <returns>\r
                ///   Whether both objects are equal.\r
                /// </returns>\r
@@ -350,14 +426,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
@@ -365,8 +441,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