//
// InternalIsValid
//
// Determine whether a name is a valid IPv6 address. Rules are:
//
// * 8 groups of 16-bit hex numbers, separated by ':'
// * a *single* run of zeros can be compressed using the symbol '::'
// * an optional string of a ScopeID delimited by '%'
// * an optional (last) 1 or 2 character prefix length field delimited by '/'
// * the last 32 bits in an address can be represented as an IPv4 address
//
// Inputs:
// <argument> name
// Domain name field of a URI to check for pattern match with
// IPv6 address
// validateStrictAddress: if set to true, it expects strict ipv6 address. Otherwise it expects
// part of the string in ipv6 format.
//
// Outputs:
// Nothing
//
// Assumes:
// the correct name is terminated by ']' character
//
// Returns:
// true if <name> has IPv6 format/ipv6 address based on validateStrictAddress, else false
//
// Throws:
// Nothing
//
// Remarks: MUST NOT be used unless all input indexes are verified and trusted.
// start must be next to '[' position, or error is reported
private static unsafe bool InternalIsValid(char *name, int start, ref int end, bool validateStrictAddress)
{
int sequenceCount = 0;
int sequenceLength = 0;
bool haveCompressor = false;
bool haveIPv4Address = false;
bool havePrefix = false;
bool expectingNumber = true;
int lastSequence = 1;
// Starting with a colon character is only valid if another colon follows.
if (name[start] == ':' && (start + 1 >= end || name[start + 1] != ':'))
{
return(false);
}
int i;
for (i = start; i < end; ++i)
{
if (havePrefix ? (name[i] >= '0' && name[i] <= '9') : Uri.IsHexDigit(name[i]))
{
++sequenceLength;
expectingNumber = false;
}
else
{
if (sequenceLength > 4)
{
return(false);
}
if (sequenceLength != 0)
{
++sequenceCount;
lastSequence = i - sequenceLength;
}
switch (name[i])
{
case '%':
while (true)
{
//accept anything in scopeID
if (++i == end)
{
// no closing ']', fail
return(false);
}
if (name[i] == ']')
{
goto case ']';
}
else if (name[i] == '/')
{
goto case '/';
}
}
case ']':
start = i;
i = end;
//this will make i = end+1
continue;
case ':':
if ((i > 0) && (name[i - 1] == ':'))
{
if (haveCompressor)
{
//
// can only have one per IPv6 address
//
return(false);
}
haveCompressor = true;
expectingNumber = false;
}
else
{
expectingNumber = true;
}
break;
case '/':
if (validateStrictAddress)
{
return(false);
}
if ((sequenceCount == 0) || havePrefix)
{
return(false);
}
havePrefix = true;
expectingNumber = true;
break;
case '.':
if (haveIPv4Address)
{
return(false);
}
i = end;
if (!IPv4AddressHelper.IsValid(name, lastSequence, ref i, true, false, false))
{
return(false);
}
// ipv4 address takes 2 slots in ipv6 address, one was just counted meeting the '.'
++sequenceCount;
haveIPv4Address = true;
--i; // it will be incremented back on the next loop
break;
default:
return(false);
}
sequenceLength = 0;
}
}
//
// if the last token was a prefix, check number of digits
//
if (havePrefix && ((sequenceLength < 1) || (sequenceLength > 2)))
{
return(false);
}
//
// these sequence counts are -1 because it is implied in end-of-sequence
//
int expectedSequenceCount = 8 + (havePrefix ? 1 : 0);
if (!expectingNumber && (sequenceLength <= 4) && (haveCompressor ? (sequenceCount < expectedSequenceCount) : (sequenceCount == expectedSequenceCount)))
{
if (i == end + 1)
{
// ']' was found
end = start + 1;
return(true);
}
return(false);
}
return(false);
}