private unsafe ushort CheckAuthorityHelper(char* pString, ushort idx, ushort length,
ref ParsingError err, ref Flags flags, UriParser syntax, ref string newHost)
{
int end = length;
char ch;
int startInput = idx;
ushort start = idx;
newHost = null;
bool justNormalized = false;
bool iriParsing = (s_IriParsing && IriParsingStatic(syntax)); // perf
bool hasUnicode = ((flags & Flags.HasUnicode) != 0); // perf
bool hostNotUnicodeNormalized = ((flags & Flags.HostUnicodeNormalized) == 0); // perf
UriSyntaxFlags syntaxFlags = syntax.Flags;
// need to build new Iri'zed string
if (hasUnicode && iriParsing && hostNotUnicodeNormalized)
{
newHost = _originalUnicodeString.Substring(0, startInput);
}
//Special case is an empty authority
if (idx == length || ((ch = pString[idx]) == '/' || (ch == '\\' && StaticIsFile(syntax)) || ch == '#' || ch == '?'))
{
if (syntax.InFact(UriSyntaxFlags.AllowEmptyHost))
{
flags &= ~Flags.UncPath; //UNC cannot have an empty hostname
if (StaticInFact(flags, Flags.ImplicitFile))
err = ParsingError.BadHostName;
else
flags |= Flags.BasicHostType;
}
else
err = ParsingError.BadHostName;
if (hasUnicode && iriParsing && hostNotUnicodeNormalized)
{
flags |= Flags.HostUnicodeNormalized;// no host
}
return idx;
}
string userInfoString = null;
// Attempt to parse user info first
if ((syntaxFlags & UriSyntaxFlags.MayHaveUserInfo) != 0)
{
for (; start < end; ++start)
{
if (start == end - 1 || pString[start] == '?' || pString[start] == '#' || pString[start] == '\\' ||
pString[start] == '/')
{
start = idx;
break;
}
else if (pString[start] == '@')
{
flags |= Flags.HasUserInfo;
// Iri'ze userinfo
if (iriParsing || (s_IdnScope != UriIdnScope.None))
{
if (iriParsing && hasUnicode && hostNotUnicodeNormalized)
{
// Normalize user info
userInfoString = IriHelper.EscapeUnescapeIri(pString, startInput, start + 1, UriComponents.UserInfo);
newHost += userInfoString;
}
else
{
userInfoString = new string(pString, startInput, start - startInput + 1);
}
}
++start;
ch = pString[start];
break;
}
}
}
// DNS name only optimization
// Fo an overridden parsing the optimization is suppressed since hostname can be changed to anything
bool dnsNotCanonical = ((syntaxFlags & UriSyntaxFlags.SimpleUserSyntax) == 0);
if (ch == '[' && syntax.InFact(UriSyntaxFlags.AllowIPv6Host)
&& IPv6AddressHelper.IsValid(pString, (int)start + 1, ref end))
{
flags |= Flags.IPv6HostType;
_iriParsing = (s_IriParsing && IriParsingStatic(syntax));
if (hasUnicode && iriParsing && hostNotUnicodeNormalized)
{
newHost += new string(pString, start, end - start);
flags |= Flags.HostUnicodeNormalized;
justNormalized = true;
}
}
else if (ch <= '9' && ch >= '0' && syntax.InFact(UriSyntaxFlags.AllowIPv4Host) &&
IPv4AddressHelper.IsValid(pString, (int)start, ref end, false, StaticNotAny(flags, Flags.ImplicitFile), syntax.InFact(UriSyntaxFlags.V1_UnknownUri)))
{
flags |= Flags.IPv4HostType;
if (hasUnicode && iriParsing && hostNotUnicodeNormalized)
{
newHost += new string(pString, start, end - start);
flags |= Flags.HostUnicodeNormalized;
justNormalized = true;
}
}
else if (((syntaxFlags & UriSyntaxFlags.AllowDnsHost) != 0) && !iriParsing &&
DomainNameHelper.IsValid(pString, start, ref end, ref dnsNotCanonical, StaticNotAny(flags, Flags.ImplicitFile)))
{
// comes here if there are only ascii chars in host with original parsing and no Iri
flags |= Flags.DnsHostType;
if (!dnsNotCanonical)
{
flags |= Flags.CanonicalDnsHost;
}
if ((s_IdnScope != UriIdnScope.None))
{
// check if intranet
//
if ((s_IdnScope == UriIdnScope.AllExceptIntranet) && IsIntranet(new string(pString, 0, end)))
{
flags |= Flags.IntranetUri;
}
if (AllowIdnStatic(syntax, flags))
{
bool allAscii = true;
bool atLeastOneIdn = false;
string idnValue = DomainNameHelper.UnicodeEquivalent(pString, start, end, ref allAscii, ref atLeastOneIdn);
// did we find at least one valid idn
if (atLeastOneIdn)
{
// need to switch string here since we didn't know beforehand there was an idn host
if (StaticNotAny(flags, Flags.HasUnicode))
_originalUnicodeString = _string; // lazily switching strings
flags |= Flags.IdnHost;
// need to build string for this special scenario
newHost = _originalUnicodeString.Substring(0, startInput) + userInfoString + idnValue;
flags |= Flags.CanonicalDnsHost;
_dnsSafeHost = new string(pString, start, end - start);
justNormalized = true;
}
flags |= Flags.HostUnicodeNormalized;
}
}
}
else if (((syntaxFlags & UriSyntaxFlags.AllowDnsHost) != 0)
&& ((syntax.InFact(UriSyntaxFlags.AllowIriParsing) && hostNotUnicodeNormalized)
|| syntax.InFact(UriSyntaxFlags.AllowIdn))
&& DomainNameHelper.IsValidByIri(pString, start, ref end, ref dnsNotCanonical,
StaticNotAny(flags, Flags.ImplicitFile)))
{
CheckAuthorityHelperHandleDnsIri(pString, start, end, startInput, iriParsing, hasUnicode, syntax,
userInfoString, ref flags, ref justNormalized, ref newHost, ref err);
}
else if ((syntaxFlags & UriSyntaxFlags.AllowUncHost) != 0)
{
//
// This must remain as the last check before BasicHost type
//
if (UncNameHelper.IsValid(pString, start, ref end, StaticNotAny(flags, Flags.ImplicitFile)))
{
if (end - start <= UncNameHelper.MaximumInternetNameLength)
flags |= Flags.UncHostType;
}
}
// The deal here is that we won't allow '\' host terminator except for the File scheme
// If we see '\' we try to make it a part of a Basic host
if (end < length && pString[end] == '\\' && (flags & Flags.HostTypeMask) != Flags.HostNotParsed
&& !StaticIsFile(syntax))
{
if (syntax.InFact(UriSyntaxFlags.V1_UnknownUri))
{
err = ParsingError.BadHostName;
flags |= Flags.UnknownHostType;
return (ushort)end;
}
flags &= ~Flags.HostTypeMask;
}
// Here we have checked the syntax up to the end of host
// The only thing that can cause an exception is the port value
// Spend some (duplicated) cycles on that.
else if (end < length && pString[end] == ':')
{
if (syntax.InFact(UriSyntaxFlags.MayHavePort))
{
int port = 0;
int startPort = end;
for (idx = (ushort)(end + 1); idx < length; ++idx)
{
ushort val = (ushort)((ushort)pString[idx] - (ushort)'0');
if ((val >= 0) && (val <= 9))
{
if ((port = (port * 10 + val)) > 0xFFFF)
break;
}
else if (val == unchecked((ushort)('/' - '0')) || val == (ushort)('?' - '0')
|| val == unchecked((ushort)('#' - '0')))
{
break;
}
else
{
// The second check is to keep compatibility with V1 until the UriParser is registered
if (syntax.InFact(UriSyntaxFlags.AllowAnyOtherHost)
&& syntax.NotAny(UriSyntaxFlags.V1_UnknownUri))
{
flags &= ~Flags.HostTypeMask;
break;
}
else
{
err = ParsingError.BadPort;
return idx;
}
}
}
// check on 0-ffff range
if (port > 0xFFFF)
{
if (syntax.InFact(UriSyntaxFlags.AllowAnyOtherHost))
{
flags &= ~Flags.HostTypeMask;
}
else
{
err = ParsingError.BadPort;
return idx;
}
}
if (iriParsing && hasUnicode && justNormalized)
{
newHost += new string(pString, startPort, idx - startPort);
}
}
else
{
flags &= ~Flags.HostTypeMask;
}
}
// check on whether nothing has worked out
if ((flags & Flags.HostTypeMask) == Flags.HostNotParsed)
{
//No user info for a Basic hostname
flags &= ~Flags.HasUserInfo;
// Some schemes do not allow HostType = Basic (plus V1 almost never understands this issue)
//
if (syntax.InFact(UriSyntaxFlags.AllowAnyOtherHost))
{
flags |= Flags.BasicHostType;
for (end = idx; end < length; ++end)
{
if (pString[end] == '/' || (pString[end] == '?' || pString[end] == '#'))
{
break;
}
}
CheckAuthorityHelperHandleAnyHostIri(pString, startInput, end, iriParsing, hasUnicode, syntax,
ref flags, ref newHost, ref err);
}
else
{
//
// ATTN V1 compat: V1 supports hostnames like ".." and ".", and so we do but only for unknown schemes.
//
if (syntax.InFact(UriSyntaxFlags.V1_UnknownUri))
{
// Can assert here that the host is not empty so we will set dotFound
// at least once or fail before exiting the loop
bool dotFound = false;
int startOtherHost = idx;
for (end = idx; end < length; ++end)
{
if (dotFound && (pString[end] == '/' || pString[end] == '?' || pString[end] == '#'))
break;
else if (end < (idx + 2) && pString[end] == '.')
{
// allow one or two dots
dotFound = true;
}
else
{
//failure
err = ParsingError.BadHostName;
flags |= Flags.UnknownHostType;
return idx;
}
}
//success
flags |= Flags.BasicHostType;
if (iriParsing && hasUnicode
&& StaticNotAny(flags, Flags.HostUnicodeNormalized))
{
// Normalize any other host
string user = new string(pString, startOtherHost, end - startOtherHost);
try
{
newHost += user.Normalize(NormalizationForm.FormC);
}
catch (ArgumentException)
{
err = ParsingError.BadFormat;
return idx;
}
flags |= Flags.HostUnicodeNormalized;
}
}
else if (syntax.InFact(UriSyntaxFlags.MustHaveAuthority) ||
(syntax.InFact(UriSyntaxFlags.MailToLikeUri)))
{
err = ParsingError.BadHostName;
flags |= Flags.UnknownHostType;
return idx;
}
}
}
return (ushort)end;
}