You've already forked linux-packaging-mono
Imported Upstream version 6.4.0.137
Former-commit-id: 943baa9f16a098c33e129777827f3a9d20da00d6
This commit is contained in:
parent
e9207cf623
commit
ef583813eb
@@ -10,15 +10,6 @@ namespace System
|
||||
// The idea is to stay with static helper methods and strings
|
||||
internal static partial class IPv4AddressHelper
|
||||
{
|
||||
internal const long Invalid = -1;
|
||||
// Note: the native parser cannot handle MaxIPv4Value, only MaxIPv4Value - 1
|
||||
private const long MaxIPv4Value = UInt32.MaxValue;
|
||||
private const int Octal = 8;
|
||||
private const int Decimal = 10;
|
||||
private const int Hex = 16;
|
||||
|
||||
private const int NumberOfLabels = 4;
|
||||
|
||||
// methods
|
||||
// Parse and canonicalize
|
||||
internal static string ParseCanonicalName(string str, int start, int end, ref bool isLoopback)
|
||||
@@ -42,298 +33,6 @@ namespace System
|
||||
}
|
||||
}
|
||||
|
||||
// Only called from the IPv6Helper, only parse the canonical format
|
||||
internal static int ParseHostNumber(string str, int start, int end)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
byte* numbers = stackalloc byte[NumberOfLabels];
|
||||
ParseCanonical(str, numbers, start, end);
|
||||
return (numbers[0] << 24) + (numbers[1] << 16) + (numbers[2] << 8) + numbers[3];
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// IsValid
|
||||
//
|
||||
// Performs IsValid on a substring. Updates the index to where we
|
||||
// believe the IPv4 address ends
|
||||
//
|
||||
// Inputs:
|
||||
// <argument> name
|
||||
// string containing possible IPv4 address
|
||||
//
|
||||
// <argument> start
|
||||
// offset in <name> to start checking for IPv4 address
|
||||
//
|
||||
// <argument> end
|
||||
// offset in <name> of the last character we can touch in the check
|
||||
//
|
||||
// Outputs:
|
||||
// <argument> end
|
||||
// index of last character in <name> we checked
|
||||
//
|
||||
// <argument> allowIPv6
|
||||
// enables parsing IPv4 addresses embedded in IPv6 address literals
|
||||
//
|
||||
// <argument> notImplicitFile
|
||||
// do not consider this URI holding an implicit filename
|
||||
//
|
||||
// <argument> unknownScheme
|
||||
// the check is made on an unknown scheme (suppress IPv4 canonicalization)
|
||||
//
|
||||
// Assumes:
|
||||
// The address string is terminated by either
|
||||
// end of the string, characters ':' '/' '\' '?'
|
||||
//
|
||||
//
|
||||
// Returns:
|
||||
// bool
|
||||
//
|
||||
// Throws:
|
||||
// Nothing
|
||||
//
|
||||
|
||||
//Remark: MUST NOT be used unless all input indexes are verified and trusted.
|
||||
internal static unsafe bool IsValid(char* name, int start, ref int end, bool allowIPv6, bool notImplicitFile, bool unknownScheme)
|
||||
{
|
||||
// IPv6 can only have canonical IPv4 embedded. Unknown schemes will not attempt parsing of non-canonical IPv4 addresses.
|
||||
if (allowIPv6 || unknownScheme)
|
||||
{
|
||||
return IsValidCanonical(name, start, ref end, allowIPv6, notImplicitFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
return ParseNonCanonical(name, start, ref end, notImplicitFile) != Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// IsValidCanonical
|
||||
//
|
||||
// Checks if the substring is a valid canonical IPv4 address or an IPv4 address embedded in an IPv6 literal
|
||||
// This is an attempt to parse ABNF productions from RFC3986, Section 3.2.2:
|
||||
// IP-literal = "[" ( IPv6address / IPvFuture ) "]"
|
||||
// IPv4address = dec-octet "." dec-octet "." dec-octet "." dec-octet
|
||||
// dec-octet = DIGIT ; 0-9
|
||||
// / %x31-39 DIGIT ; 10-99
|
||||
// / "1" 2DIGIT ; 100-199
|
||||
// / "2" %x30-34 DIGIT ; 200-249
|
||||
// / "25" %x30-35 ; 250-255
|
||||
//
|
||||
internal static unsafe bool IsValidCanonical(char* name, int start, ref int end, bool allowIPv6, bool notImplicitFile)
|
||||
{
|
||||
int dots = 0;
|
||||
int number = 0;
|
||||
bool haveNumber = false;
|
||||
bool firstCharIsZero = false;
|
||||
|
||||
while (start < end)
|
||||
{
|
||||
char ch = name[start];
|
||||
if (allowIPv6)
|
||||
{
|
||||
// for ipv4 inside ipv6 the terminator is either ScopeId, prefix or ipv6 terminator
|
||||
if (ch == ']' || ch == '/' || ch == '%') break;
|
||||
}
|
||||
else if (ch == '/' || ch == '\\' || (notImplicitFile && (ch == ':' || ch == '?' || ch == '#')))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (ch <= '9' && ch >= '0')
|
||||
{
|
||||
if (!haveNumber && (ch == '0'))
|
||||
{
|
||||
if ((start + 1 < end) && name[start + 1] == '0')
|
||||
{
|
||||
// 00 is not allowed as a prefix.
|
||||
return false;
|
||||
}
|
||||
|
||||
firstCharIsZero = true;
|
||||
}
|
||||
|
||||
haveNumber = true;
|
||||
number = number * 10 + (name[start] - '0');
|
||||
if (number > 255)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (ch == '.')
|
||||
{
|
||||
if (!haveNumber || (number > 0 && firstCharIsZero))
|
||||
{
|
||||
// 0 is not allowed to prefix a number.
|
||||
return false;
|
||||
}
|
||||
++dots;
|
||||
haveNumber = false;
|
||||
number = 0;
|
||||
firstCharIsZero = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
++start;
|
||||
}
|
||||
bool res = (dots == 3) && haveNumber;
|
||||
if (res)
|
||||
{
|
||||
end = start;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// Parse any canonical or non-canonical IPv4 formats and return a long between 0 and MaxIPv4Value.
|
||||
// Return Invalid (-1) for failures.
|
||||
// If the address has less than three dots, only the rightmost section is assumed to contain the combined value for
|
||||
// the missing sections: 0xFF00FFFF == 0xFF.0x00.0xFF.0xFF == 0xFF.0xFFFF
|
||||
internal static unsafe long ParseNonCanonical(char* name, int start, ref int end, bool notImplicitFile)
|
||||
{
|
||||
int numberBase = Decimal;
|
||||
char ch;
|
||||
Span<long> parts = stackalloc long[4];
|
||||
long currentValue = 0;
|
||||
bool atLeastOneChar = false;
|
||||
|
||||
// Parse one dotted section at a time
|
||||
int dotCount = 0; // Limit 3
|
||||
int current = start;
|
||||
for (; current < end; current++)
|
||||
{
|
||||
ch = name[current];
|
||||
currentValue = 0;
|
||||
|
||||
// Figure out what base this section is in
|
||||
numberBase = Decimal;
|
||||
if (ch == '0')
|
||||
{
|
||||
numberBase = Octal;
|
||||
current++;
|
||||
atLeastOneChar = true;
|
||||
if (current < end)
|
||||
{
|
||||
ch = name[current];
|
||||
if (ch == 'x' || ch == 'X')
|
||||
{
|
||||
numberBase = Hex;
|
||||
current++;
|
||||
atLeastOneChar = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse this section
|
||||
for (; current < end; current++)
|
||||
{
|
||||
ch = name[current];
|
||||
int digitValue;
|
||||
|
||||
if ((numberBase == Decimal || numberBase == Hex) && '0' <= ch && ch <= '9')
|
||||
{
|
||||
digitValue = ch - '0';
|
||||
}
|
||||
else if (numberBase == Octal && '0' <= ch && ch <= '7')
|
||||
{
|
||||
digitValue = ch - '0';
|
||||
}
|
||||
else if (numberBase == Hex && 'a' <= ch && ch <= 'f')
|
||||
{
|
||||
digitValue = ch + 10 - 'a';
|
||||
}
|
||||
else if (numberBase == Hex && 'A' <= ch && ch <= 'F')
|
||||
{
|
||||
digitValue = ch + 10 - 'A';
|
||||
}
|
||||
else
|
||||
{
|
||||
break; // Invalid/terminator
|
||||
}
|
||||
|
||||
currentValue = (currentValue * numberBase) + digitValue;
|
||||
|
||||
if (currentValue > MaxIPv4Value) // Overflow
|
||||
{
|
||||
return Invalid;
|
||||
}
|
||||
|
||||
atLeastOneChar = true;
|
||||
}
|
||||
|
||||
if (current < end && name[current] == '.')
|
||||
{
|
||||
if (dotCount >= 3 // Max of 3 dots and 4 segments
|
||||
|| !atLeastOneChar // No empty segments: 1...1
|
||||
// Only the last segment can be more than 255 (if there are less than 3 dots)
|
||||
|| currentValue > 0xFF)
|
||||
{
|
||||
return Invalid;
|
||||
}
|
||||
parts[dotCount] = currentValue;
|
||||
dotCount++;
|
||||
atLeastOneChar = false;
|
||||
continue;
|
||||
}
|
||||
// We don't get here unless We find an invalid character or a terminator
|
||||
break;
|
||||
}
|
||||
|
||||
// Terminators
|
||||
if (!atLeastOneChar)
|
||||
{
|
||||
return Invalid; // Empty trailing segment: 1.1.1.
|
||||
}
|
||||
else if (current >= end)
|
||||
{
|
||||
// end of string, allowed
|
||||
}
|
||||
else if ((ch = name[current]) == '/' || ch == '\\' || (notImplicitFile && (ch == ':' || ch == '?' || ch == '#')))
|
||||
{
|
||||
end = current;
|
||||
}
|
||||
else
|
||||
{
|
||||
// not a valid terminating character
|
||||
return Invalid;
|
||||
}
|
||||
|
||||
parts[dotCount] = currentValue;
|
||||
|
||||
// Parsed, reassemble and check for overflows
|
||||
switch (dotCount)
|
||||
{
|
||||
case 0: // 0xFFFFFFFF
|
||||
if (parts[0] > MaxIPv4Value)
|
||||
{
|
||||
return Invalid;
|
||||
}
|
||||
return parts[0];
|
||||
case 1: // 0xFF.0xFFFFFF
|
||||
if (parts[1] > 0xffffff)
|
||||
{
|
||||
return Invalid;
|
||||
}
|
||||
return (parts[0] << 24) | (parts[1] & 0xffffff);
|
||||
case 2: // 0xFF.0xFF.0xFFFF
|
||||
if (parts[2] > 0xffff)
|
||||
{
|
||||
return Invalid;
|
||||
}
|
||||
return (parts[0] << 24) | ((parts[1] & 0xff) << 16) | (parts[2] & 0xffff);
|
||||
case 3: // 0xFF.0xFF.0xFF.0xFF
|
||||
if (parts[3] > 0xff)
|
||||
{
|
||||
return Invalid;
|
||||
}
|
||||
return (parts[0] << 24) | ((parts[1] & 0xff) << 16) | ((parts[2] & 0xff) << 8) | (parts[3] & 0xff);
|
||||
default:
|
||||
return Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Parse
|
||||
//
|
||||
@@ -359,26 +58,5 @@ namespace System
|
||||
|
||||
return numbers[0] == 127;
|
||||
}
|
||||
|
||||
// Assumes:
|
||||
// <Name> has been validated and contains only decimal digits in groups
|
||||
// of 8-bit numbers and the characters '.'
|
||||
// Address may terminate with ':' or with the end of the string
|
||||
//
|
||||
private static unsafe bool ParseCanonical(string name, byte* numbers, int start, int end)
|
||||
{
|
||||
for (int i = 0; i < NumberOfLabels; ++i)
|
||||
{
|
||||
byte b = 0;
|
||||
char ch;
|
||||
for (; (start < end) && (ch = name[start]) != '.' && ch != ':'; ++start)
|
||||
{
|
||||
b = (byte)(b * 10 + (byte)(ch - '0'));
|
||||
}
|
||||
numbers[i] = b;
|
||||
++start;
|
||||
}
|
||||
return numbers[0] == 127;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,7 @@
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Globalization;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace System
|
||||
{
|
||||
@@ -12,108 +11,111 @@ namespace System
|
||||
// The idea is to stay with static helper methods and strings
|
||||
internal static partial class IPv6AddressHelper
|
||||
{
|
||||
// fields
|
||||
#if !MONO
|
||||
private const int NumberOfLabels = 8;
|
||||
#endif
|
||||
// Lower case hex, no leading zeros
|
||||
private const string CanonicalNumberFormat = "{0:x}";
|
||||
private const string EmbeddedIPv4Format = ":{0:d}.{1:d}.{2:d}.{3:d}";
|
||||
private const char Separator = ':';
|
||||
|
||||
// methods
|
||||
|
||||
internal static string ParseCanonicalName(string str, int start, ref bool isLoopback, ref string scopeId)
|
||||
internal static unsafe string ParseCanonicalName(string str, int start, ref bool isLoopback, ref string scopeId)
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
ushort* numbers = stackalloc ushort[NumberOfLabels];
|
||||
// optimized zeroing of 8 shorts = 2 longs
|
||||
((long*)numbers)[0] = 0L;
|
||||
((long*)numbers)[1] = 0L;
|
||||
isLoopback = Parse(str, numbers, start, ref scopeId);
|
||||
return '[' + CreateCanonicalName(numbers) + ']';
|
||||
}
|
||||
}
|
||||
ushort* numbersPtr = stackalloc ushort[NumberOfLabels];
|
||||
// optimized zeroing of 8 shorts = 2 longs
|
||||
((long*)numbersPtr)[0] = 0L;
|
||||
((long*)numbersPtr)[1] = 0L;
|
||||
Span<ushort> numbers = new Span<ushort>(numbersPtr, NumberOfLabels);
|
||||
Parse(str, numbersPtr, start, ref scopeId);
|
||||
isLoopback = IsLoopback(numbers);
|
||||
|
||||
internal static unsafe string CreateCanonicalName(ushort* numbers)
|
||||
{
|
||||
// RFC 5952 Sections 4 & 5 - Compressed, lower case, with possible embedded IPv4 addresses.
|
||||
|
||||
// Start to finish, inclusive. <-1, -1> for no compression
|
||||
KeyValuePair<int, int> range = FindCompressionRange(numbers);
|
||||
(int rangeStart, int rangeEnd) = FindCompressionRange(numbers);
|
||||
bool ipv4Embedded = ShouldHaveIpv4Embedded(numbers);
|
||||
|
||||
StringBuilder builder = new StringBuilder();
|
||||
Span<char> stackSpace = stackalloc char[48]; // large enough for any IPv6 string, including brackets
|
||||
stackSpace[0] = '[';
|
||||
int pos = 1;
|
||||
int charsWritten;
|
||||
bool success;
|
||||
for (int i = 0; i < NumberOfLabels; i++)
|
||||
{
|
||||
if (ipv4Embedded && i == (NumberOfLabels - 2))
|
||||
{
|
||||
stackSpace[pos++] = ':';
|
||||
|
||||
// Write the remaining digits as an IPv4 address
|
||||
builder.AppendFormat(CultureInfo.InvariantCulture, EmbeddedIPv4Format,
|
||||
numbers[i] >> 8, numbers[i] & 0xFF, numbers[i + 1] >> 8, numbers[i + 1] & 0xFF);
|
||||
success = (numbers[i] >> 8).TryFormat(stackSpace.Slice(pos), out charsWritten);
|
||||
Debug.Assert(success);
|
||||
pos += charsWritten;
|
||||
|
||||
stackSpace[pos++] = '.';
|
||||
success = (numbers[i] & 0xFF).TryFormat(stackSpace.Slice(pos), out charsWritten);
|
||||
Debug.Assert(success);
|
||||
pos += charsWritten;
|
||||
|
||||
stackSpace[pos++] = '.';
|
||||
success = (numbers[i + 1] >> 8).TryFormat(stackSpace.Slice(pos), out charsWritten);
|
||||
Debug.Assert(success);
|
||||
pos += charsWritten;
|
||||
|
||||
stackSpace[pos++] = '.';
|
||||
success = (numbers[i + 1] & 0xFF).TryFormat(stackSpace.Slice(pos), out charsWritten);
|
||||
Debug.Assert(success);
|
||||
pos += charsWritten;
|
||||
break;
|
||||
}
|
||||
|
||||
// Compression; 1::1, ::1, 1::
|
||||
if (range.Key == i)
|
||||
{ // Start compression, add :
|
||||
builder.Append(Separator);
|
||||
if (rangeStart == i)
|
||||
{
|
||||
// Start compression, add :
|
||||
stackSpace[pos++] = ':';
|
||||
}
|
||||
if (range.Key <= i && range.Value == (NumberOfLabels - 1))
|
||||
{ // Remainder compressed; 1::
|
||||
builder.Append(Separator);
|
||||
|
||||
if (rangeStart <= i && rangeEnd == NumberOfLabels)
|
||||
{
|
||||
// Remainder compressed; 1::
|
||||
stackSpace[pos++] = ':';
|
||||
break;
|
||||
}
|
||||
if (range.Key <= i && i <= range.Value)
|
||||
|
||||
if (rangeStart <= i && i < rangeEnd)
|
||||
{
|
||||
continue; // Compressed
|
||||
}
|
||||
|
||||
if (i != 0)
|
||||
{
|
||||
builder.Append(Separator);
|
||||
stackSpace[pos++] = ':';
|
||||
}
|
||||
builder.AppendFormat(CultureInfo.InvariantCulture, CanonicalNumberFormat, numbers[i]);
|
||||
success = numbers[i].TryFormat(stackSpace.Slice(pos), out charsWritten, format: "x");
|
||||
Debug.Assert(success);
|
||||
pos += charsWritten;
|
||||
}
|
||||
|
||||
return builder.ToString();
|
||||
stackSpace[pos++] = ']';
|
||||
return new string(stackSpace.Slice(0, pos));
|
||||
}
|
||||
|
||||
// RFC 5952 Section 4.2.3
|
||||
// Longest consecutive sequence of zero segments, minimum 2.
|
||||
// On equal, first sequence wins.
|
||||
// <-1, -1> for no compression.
|
||||
private static unsafe KeyValuePair<int, int> FindCompressionRange(ushort* numbers)
|
||||
private static unsafe bool IsLoopback(ReadOnlySpan<ushort> numbers)
|
||||
{
|
||||
int longestSequenceLength = 0;
|
||||
int longestSequenceStart = -1;
|
||||
//
|
||||
// is the address loopback? Loopback is defined as one of:
|
||||
//
|
||||
// 0:0:0:0:0:0:0:1
|
||||
// 0:0:0:0:0:0:127.0.0.1 == 0:0:0:0:0:0:7F00:0001
|
||||
// 0:0:0:0:0:FFFF:127.0.0.1 == 0:0:0:0:0:FFFF:7F00:0001
|
||||
//
|
||||
|
||||
int currentSequenceLength = 0;
|
||||
for (int i = 0; i < NumberOfLabels; i++)
|
||||
{
|
||||
if (numbers[i] == 0)
|
||||
{ // In a sequence
|
||||
currentSequenceLength++;
|
||||
if (currentSequenceLength > longestSequenceLength)
|
||||
{
|
||||
longestSequenceLength = currentSequenceLength;
|
||||
longestSequenceStart = i - currentSequenceLength + 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
currentSequenceLength = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (longestSequenceLength >= 2)
|
||||
{
|
||||
return new KeyValuePair<int, int>(longestSequenceStart,
|
||||
longestSequenceStart + longestSequenceLength - 1);
|
||||
}
|
||||
|
||||
return new KeyValuePair<int, int>(-1, -1); // No compression
|
||||
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] == 0x7F00)
|
||||
&& (numbers[7] == 0x0001))
|
||||
&& ((numbers[5] == 0)
|
||||
|| (numbers[5] == 0xFFFF))));
|
||||
}
|
||||
|
||||
// Returns true if the IPv6 address should be formated with an embedded IPv4 address:
|
||||
@@ -351,210 +353,5 @@ namespace System
|
||||
{
|
||||
return InternalIsValid(name, start, ref end, false);
|
||||
}
|
||||
|
||||
//
|
||||
// Parse
|
||||
//
|
||||
// Convert this IPv6 address into a sequence of 8 16-bit numbers
|
||||
//
|
||||
// Inputs:
|
||||
// <member> Name
|
||||
// The validated IPv6 address
|
||||
//
|
||||
// Outputs:
|
||||
// <member> numbers
|
||||
// Array filled in with the numbers in the IPv6 groups
|
||||
//
|
||||
// <member> PrefixLength
|
||||
// Set to the number after the prefix separator (/) if found
|
||||
//
|
||||
// Assumes:
|
||||
// <Name> has been validated and contains only hex digits in groups of
|
||||
// 16-bit numbers, the characters ':' and '/', and a possible IPv4
|
||||
// address
|
||||
//
|
||||
// Returns:
|
||||
// true if this is a loopback, false otherwise. There is no failure indication as the sting must be a valid one.
|
||||
//
|
||||
// Throws:
|
||||
// Nothing
|
||||
//
|
||||
|
||||
internal static unsafe bool Parse(string address, ushort* numbers, int start, ref string scopeId)
|
||||
{
|
||||
int number = 0;
|
||||
int index = 0;
|
||||
int compressorIndex = -1;
|
||||
bool numberIsValid = true;
|
||||
|
||||
//This used to be a class instance member but have not been used so far
|
||||
int PrefixLength = 0;
|
||||
if (address[start] == '[')
|
||||
{
|
||||
++start;
|
||||
}
|
||||
|
||||
for (int i = start; i < address.Length && address[i] != ']';)
|
||||
{
|
||||
switch (address[i])
|
||||
{
|
||||
case '%':
|
||||
if (numberIsValid)
|
||||
{
|
||||
numbers[index++] = (ushort)number;
|
||||
numberIsValid = false;
|
||||
}
|
||||
|
||||
start = i;
|
||||
for (++i; address[i] != ']' && address[i] != '/'; ++i)
|
||||
{
|
||||
;
|
||||
}
|
||||
scopeId = address.Substring(start, i - start);
|
||||
// ignore prefix if any
|
||||
for (; address[i] != ']'; ++i)
|
||||
{
|
||||
;
|
||||
}
|
||||
break;
|
||||
|
||||
case ':':
|
||||
numbers[index++] = (ushort)number;
|
||||
number = 0;
|
||||
++i;
|
||||
if (address[i] == ':')
|
||||
{
|
||||
compressorIndex = index;
|
||||
++i;
|
||||
}
|
||||
else if ((compressorIndex < 0) && (index < 6))
|
||||
{
|
||||
//
|
||||
// no point checking for IPv4 address if we don't
|
||||
// have a compressor or we haven't seen 6 16-bit
|
||||
// numbers yet
|
||||
//
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
//
|
||||
// check to see if the upcoming number is really an IPv4
|
||||
// address. If it is, convert it to 2 ushort numbers
|
||||
//
|
||||
|
||||
for (int j = i; (address[j] != ']') &&
|
||||
(address[j] != ':') &&
|
||||
(address[j] != '%') &&
|
||||
(address[j] != '/') &&
|
||||
(j < i + 4); ++j)
|
||||
{
|
||||
if (address[j] == '.')
|
||||
{
|
||||
//
|
||||
// we have an IPv4 address. Find the end of it:
|
||||
// we know that since we have a valid IPv6
|
||||
// address, the only things that will terminate
|
||||
// the IPv4 address are the prefix delimiter '/'
|
||||
// or the end-of-string (which we conveniently
|
||||
// delimited with ']')
|
||||
//
|
||||
|
||||
while ((address[j] != ']') && (address[j] != '/') && (address[j] != '%'))
|
||||
{
|
||||
++j;
|
||||
}
|
||||
number = IPv4AddressHelper.ParseHostNumber(address, i, j);
|
||||
unchecked
|
||||
{
|
||||
numbers[index++] = (ushort)(number >> 16);
|
||||
numbers[index++] = (ushort)number;
|
||||
}
|
||||
i = j;
|
||||
|
||||
//
|
||||
// set this to avoid adding another number to
|
||||
// the array if there's a prefix
|
||||
//
|
||||
|
||||
number = 0;
|
||||
numberIsValid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case '/':
|
||||
if (numberIsValid)
|
||||
{
|
||||
numbers[index++] = (ushort)number;
|
||||
numberIsValid = false;
|
||||
}
|
||||
|
||||
//
|
||||
// since we have a valid IPv6 address string, the prefix
|
||||
// length is the last token in the string
|
||||
//
|
||||
|
||||
for (++i; address[i] != ']'; ++i)
|
||||
{
|
||||
PrefixLength = PrefixLength * 10 + (address[i] - '0');
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
number = number * 16 + Uri.FromHex(address[i++]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// add number to the array if its not the prefix length or part of
|
||||
// an IPv4 address that's already been handled
|
||||
//
|
||||
|
||||
if (numberIsValid)
|
||||
{
|
||||
numbers[index++] = (ushort)number;
|
||||
}
|
||||
|
||||
//
|
||||
// if we had a compressor sequence ("::") then we need to expand the
|
||||
// numbers array
|
||||
//
|
||||
|
||||
if (compressorIndex > 0)
|
||||
{
|
||||
int toIndex = NumberOfLabels - 1;
|
||||
int fromIndex = index - 1;
|
||||
|
||||
for (int i = index - compressorIndex; i > 0; --i)
|
||||
{
|
||||
numbers[toIndex--] = numbers[fromIndex];
|
||||
numbers[fromIndex--] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// is the address loopback? Loopback is defined as one of:
|
||||
//
|
||||
// 0:0:0:0:0:0:0:1
|
||||
// 0:0:0:0:0:0:127.0.0.1 == 0:0:0:0:0:0:7F00:0001
|
||||
// 0:0:0:0:0:FFFF:127.0.0.1 == 0:0:0:0:0:FFFF:7F00:0001
|
||||
//
|
||||
|
||||
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] == 0x7F00)
|
||||
&& (numbers[7] == 0x0001))
|
||||
&& ((numbers[5] == 0)
|
||||
|| (numbers[5] == 0xFFFF))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user