The Labs \ Source Viewer \ SSCLI \ System \ IPv6AddressHelper

  1. //------------------------------------------------------------------------------
  2. // <copyright file="_IPv6Address.cs" company="Microsoft">
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. // </copyright>
  14. //------------------------------------------------------------------------------
  15. namespace System
  16. {
  17.    
  18.     // The class designed as to keep minimal the working set of Uri class.
  19.     // The idea is to stay with static helper methods and strings
  20.     internal class IPv6AddressHelper
  21.     {
  22.        
  23.         // fields
  24.        
  25.         private const int NumberOfLabels = 8;
  26.         private const string CanonicalNumberFormat = "{0:X4}";
  27.        
  28.         private IPv6AddressHelper()
  29.         {
  30.         }
  31.        
  32.         // methods
  33.        
  34.         static internal string ParseCanonicalName(string str, int start, ref bool isLoopback, ref string scopeId)
  35.         {
  36.             unsafe {
  37.                 ushort* numbers = stackalloc ushort[NumberOfLabels];
  38.                 // optimized zeroing of 8 shorts = 2 longs
  39.                 ((long*)numbers)[0] = 0l;
  40.                 ((long*)numbers)[1] = 0l;
  41.                 isLoopback = Parse(str, numbers, start, ref scopeId);
  42.                 return CreateCanonicalName(numbers);
  43.             }
  44.         }
  45.        
  46.         unsafe private static string CreateCanonicalName(ushort* numbers)
  47.         {
  48.             return '[' + String.Format(CanonicalNumberFormat, numbers[0]) + ':' + String.Format(CanonicalNumberFormat, numbers[1]) + ':' + String.Format(CanonicalNumberFormat, numbers[2]) + ':' + String.Format(CanonicalNumberFormat, numbers[3]) + ':' + String.Format(CanonicalNumberFormat, numbers[4]) + ':' + String.Format(CanonicalNumberFormat, numbers[5]) + ':' + String.Format(CanonicalNumberFormat, numbers[6]) + ':' + String.Format(CanonicalNumberFormat, numbers[7]) + ']';
  49.         }
  50.        
  51.         //
  52.         // IsValid
  53.         //
  54.         // Determine whether a name is a valid IPv6 address. Rules are:
  55.         //
  56.         // * 8 groups of 16-bit hex numbers, separated by ':'
  57.         // * a *single* run of zeros can be compressed using the symbol '::'
  58.         // * an optional string of a ScopeID delimited by '%'
  59.         // * an optional (last) 1 or 2 character prefix length field delimited by '/'
  60.         // * the last 32 bits in an address can be represented as an IPv4 address
  61.         //
  62.         // Inputs:
  63.         // <argument> name
  64.         // Domain name field of a URI to check for pattern match with
  65.         // IPv6 address
  66.         //
  67.         // Outputs:
  68.         // Nothing
  69.         //
  70.         // Assumes:
  71.         // the correct name is terminated by ']' character
  72.         //
  73.         // Returns:
  74.         // true if <name> has IPv6 format, else false
  75.         //
  76.         // Throws:
  77.         // Nothing
  78.         //
  79.        
  80.         // Remarks: MUST NOT be used unless all input indexes are are verified and trusted.
  81.         // start must be next to '[' position, or error is reported
  82.         unsafe static internal bool IsValid(char* name, int start, ref int end)
  83.         {
  84.            
  85.             int sequenceCount = 0;
  86.             int sequenceLength = 0;
  87.             bool haveCompressor = false;
  88.             bool haveIPv4Address = false;
  89.             bool havePrefix = false;
  90.             bool expectingNumber = true;
  91.             int lastSequence = 1;
  92.            
  93.             int i;
  94.             for (i = start; i < end; ++i) {
  95.                 if (havePrefix ? (name[i] >= '0' && name[i] <= '9') : Uri.IsHexDigit(name[i])) {
  96.                     ++sequenceLength;
  97.                     expectingNumber = false;
  98.                 }
  99.                 else {
  100.                     if (sequenceLength > 4) {
  101.                         return false;
  102.                     }
  103.                     if (sequenceLength != 0) {
  104.                         ++sequenceCount;
  105.                         lastSequence = i - sequenceLength;
  106.                     }
  107.                     switch (name[i]) {
  108.                         case '%':
  109.                             while (true) {
  110.                                 //accept anything in scopeID
  111.                                 if (++i == end) {
  112.                                     // no closing ']', fail
  113.                                     return false;
  114.                                 }
  115.                                 if (name[i] == ']') {
  116.                                     goto case ']';
  117.                                 }
  118.                                 else if (name[i] == '/') {
  119.                                     goto case '/';
  120.                                 }
  121.                             }
  122.                             break;
  123.                         case ']':
  124.                             start = i;
  125.                             i = end;
  126.                             //this will make i = end+1
  127.                             continue;
  128.                         case ':':
  129.                             if ((i > 0) && (name[i - 1] == ':')) {
  130.                                 if (haveCompressor) {
  131.                                    
  132.                                     //
  133.                                     // can only have one per IPv6 address
  134.                                     //
  135.                                    
  136.                                     return false;
  137.                                 }
  138.                                 haveCompressor = true;
  139.                                 expectingNumber = false;
  140.                             }
  141.                             else {
  142.                                 expectingNumber = true;
  143.                             }
  144.                             break;
  145.                         case '/':
  146.                            
  147.                             if ((sequenceCount == 0) || havePrefix) {
  148.                                 return false;
  149.                             }
  150.                             havePrefix = true;
  151.                             expectingNumber = true;
  152.                             break;
  153.                         case '.':
  154.                            
  155.                             if (haveIPv4Address) {
  156.                                 return false;
  157.                             }
  158.                            
  159.                             i = end;
  160.                             if (!IPv4AddressHelper.IsValid(name, lastSequence, ref i, true, false)) {
  161.                                 return false;
  162.                             }
  163.                             // ipv4 address takes 2 slots in ipv6 address, one was just counted meeting the '.'
  164.                             ++sequenceCount;
  165.                             haveIPv4Address = true;
  166.                             --i;
  167.                             // it will be incremented back on the next loop
  168.                             break;
  169.                         default:
  170.                            
  171.                             return false;
  172.                     }
  173.                     sequenceLength = 0;
  174.                 }
  175.             }
  176.            
  177.             //
  178.             // if the last token was a prefix, check number of digits
  179.             //
  180.            
  181.             if (havePrefix && ((sequenceLength < 1) || (sequenceLength > 2))) {
  182.                 return false;
  183.             }
  184.            
  185.             //
  186.             // these sequence counts are -1 because it is implied in end-of-sequence
  187.             //
  188.            
  189.             int expectedSequenceCount = 8 + (havePrefix ? 1 : 0);
  190.            
  191.             if (!expectingNumber && (sequenceLength <= 4) && (haveCompressor ? (sequenceCount < expectedSequenceCount) : (sequenceCount == expectedSequenceCount))) {
  192.                 if (i == end + 1) {
  193.                     // ']' was found
  194.                     end = start + 1;
  195.                     return true;
  196.                 }
  197.                 return false;
  198.             }
  199.             return false;
  200.         }
  201.        
  202.         //
  203.         // Parse
  204.         //
  205.         // Convert this IPv6 address into a sequence of 8 16-bit numbers
  206.         //
  207.         // Inputs:
  208.         // <member> Name
  209.         // The validated IPv6 address
  210.         //
  211.         // Outputs:
  212.         // <member> numbers
  213.         // Array filled in with the numbers in the IPv6 groups
  214.         //
  215.         // <member> PrefixLength
  216.         // Set to the number after the prefix separator (/) if found
  217.         //
  218.         // Assumes:
  219.         // <Name> has been validated and contains only hex digits in groups of
  220.         // 16-bit numbers, the characters ':' and '/', and a possible IPv4
  221.         // address
  222.         //
  223.         // Returns:
  224.         // true if this is a loopback, false otherwise. There is no falure indication as the sting must be a valid one.
  225.         //
  226.         // Throws:
  227.         // Nothing
  228.         //
  229.        
  230.         unsafe static internal bool Parse(string address, ushort* numbers, int start, ref string scopeId)
  231.         {
  232.            
  233.             int number = 0;
  234.             int index = 0;
  235.             int compressorIndex = -1;
  236.             bool numberIsValid = true;
  237.            
  238.             //This used to be a class instance member but have not been used so far
  239.             int PrefixLength = 0;
  240.             if (address[start] == '[') {
  241.                 ++start;
  242.             }
  243.            
  244.             for (int i = start; i < address.Length && address[i] != ']';) {
  245.                 switch (address[i]) {
  246.                     case '%':
  247.                         if (numberIsValid) {
  248.                             numbers[index++] = (ushort)number;
  249.                             numberIsValid = false;
  250.                         }
  251.                        
  252.                         start = i;
  253.                         for (++i; address[i] != ']' && address[i] != '/'; ++i) {
  254.                             ;
  255.                         }
  256.                         scopeId = address.Substring(start, i - start);
  257.                         // ignore prefix if any
  258.                         for (; address[i] != ']'; ++i) {
  259.                             ;
  260.                         }
  261.                         break;
  262.                     case ':':
  263.                        
  264.                         numbers[index++] = (ushort)number;
  265.                         number = 0;
  266.                         ++i;
  267.                         if (address[i] == ':') {
  268.                             compressorIndex = index;
  269.                             ++i;
  270.                         }
  271.                         else if ((compressorIndex < 0) && (index < 6)) {
  272.                            
  273.                             //
  274.                             // no point checking for IPv4 address if we don't
  275.                             // have a compressor or we haven't seen 6 16-bit
  276.                             // numbers yet
  277.                             //
  278.                            
  279.                             break;
  280.                         }
  281.                        
  282.                         //
  283.                         // check to see if the upcoming number is really an IPv4
  284.                         // address. If it is, convert it to 2 ushort numbers
  285.                         //
  286.                        
  287.                         for (int j = i; (address[j] != ']') && (address[j] != ':') && (address[j] != '%') && (address[j] != '/') && (j < i + 4); ++j) {
  288.                            
  289.                             if (address[j] == '.') {
  290.                                
  291.                                 //
  292.                                 // we have an IPv4 address. Find the end of it:
  293.                                 // we know that since we have a valid IPv6
  294.                                 // address, the only things that will terminate
  295.                                 // the IPv4 address are the prefix delimiter '/'
  296.                                 // or the end-of-string (which we conveniently
  297.                                 // delimited with ']')
  298.                                 //
  299.                                
  300.                                 while ((address[j] != ']') && (address[j] != '/') && (address[j] != '%')) {
  301.                                     ++j;
  302.                                 }
  303.                                 number = IPv4AddressHelper.ParseHostNumber(address, i, j);
  304.                                 numbers[index++] = (ushort)(number >> 16);
  305.                                 numbers[index++] = (ushort)number;
  306.                                 i = j;
  307.                                
  308.                                 //
  309.                                 // set this to avoid adding another number to
  310.                                 // the array if there's a prefix
  311.                                 //
  312.                                
  313.                                 number = 0;
  314.                                 numberIsValid = false;
  315.                                 break;
  316.                             }
  317.                         }
  318.                         break;
  319.                     case '/':
  320.                        
  321.                         if (numberIsValid) {
  322.                             numbers[index++] = (ushort)number;
  323.                             numberIsValid = false;
  324.                         }
  325.                        
  326.                         //
  327.                         // since we have a valid IPv6 address string, the prefix
  328.                         // length is the last token in the string
  329.                         //
  330.                        
  331.                         for (++i; address[i] != ']'; ++i) {
  332.                             PrefixLength = PrefixLength * 10 + (address[i] - '0');
  333.                         }
  334.                         break;
  335.                     default:
  336.                        
  337.                         number = number * 16 + Uri.FromHex(address[i++]);
  338.                         break;
  339.                 }
  340.             }
  341.            
  342.             //
  343.             // add number to the array if its not the prefix length or part of
  344.             // an IPv4 address that's already been handled
  345.             //
  346.            
  347.             if (numberIsValid) {
  348.                 numbers[index++] = (ushort)number;
  349.             }
  350.            
  351.             //
  352.             // if we had a compressor sequence ("::") then we need to expand the
  353.             // numbers array
  354.             //
  355.            
  356.             if (compressorIndex > 0) {
  357.                
  358.                 int toIndex = NumberOfLabels - 1;
  359.                 int fromIndex = index - 1;
  360.                
  361.                 for (int i = index - compressorIndex; i > 0; --i) {
  362.                     numbers[toIndex--] = numbers[fromIndex];
  363.                     numbers[fromIndex--] = 0;
  364.                 }
  365.             }
  366.            
  367.             //
  368.             // is the address loopback? Loopback is defined as one of:
  369.             //
  370.             // 0:0:0:0:0:0:0:1
  371.             // 0:0:0:0:0:0:127.0.0.1 == 0:0:0:0:0:0:7F00:0001
  372.             // 0:0:0:0:0:FFFF:127.0.0.1 == 0:0:0:0:0:FFFF:7F00:0001
  373.             //
  374.            
  375.             return ((numbers[0] == 0) && (numbers[1] == 0) && (numbers[2] == 0) && (numbers[3] == 0) && (numbers[4] == 0)) && (((numbers[5] == 0) && (numbers[6] == 0) && (numbers[7] == 1)) || (((numbers[6] == 32512) && (numbers[7] == 1)) && ((numbers[5] == 0) || (numbers[5] == 65535))));
  376.            
  377.         }
  378.     }
  379. }

Developer Fusion