/// <summary>Converts a host name into its idn equivalent.</summary> internal static string IdnEquivalent(string hostname) { if (hostname.Length == 0) { return(hostname); } // check if only ascii chars // special case since idnmapping will not lowercase if only ascii present bool allAscii = true; foreach (char c in hostname) { if (c > 0x7F) { allAscii = false; break; } } if (allAscii) { // just lowercase for ascii return(hostname.ToLowerInvariant()); } string bidiStrippedHost = UriHelper.StripBidiControlCharacters(hostname, hostname); try { string asciiForm = s_idnMapping.GetAscii(bidiStrippedHost); if (ContainsCharactersUnsafeForNormalizedHost(asciiForm)) { throw new UriFormatException(SR.net_uri_BadUnicodeHostForIdn); } return(asciiForm); } catch (ArgumentException) { throw new UriFormatException(SR.net_uri_BadUnicodeHostForIdn); } }
internal static unsafe string?UnicodeEquivalent(char *hostname, int start, int end, ref bool allAscii, ref bool atLeastOneValidIdn) { // hostname already validated allAscii = true; atLeastOneValidIdn = false; string?idn = null; if (end <= start) { return(idn); } string unescapedHostname = UriHelper.StripBidiControlCharacters(new ReadOnlySpan <char>(hostname + start, end - start)); string?unicodeEqvlHost = null; int curPos = 0; int newPos = 0; int length = unescapedHostname.Length; bool asciiLabel = true; bool foundAce = false; bool checkedAce = false; bool foundDot = false; // We run a loop where for every label // a) if label is ascii and no ace then we lowercase it // b) if label is ascii and ace and not valid idn then just lowercase it // c) if label is ascii and ace and is valid idn then get its unicode eqvl // d) if label is unicode then clean it by running it through idnmapping do { asciiLabel = true; foundAce = false; checkedAce = false; foundDot = false; //find the dot or hit the end newPos = curPos; while (newPos < length) { char c = unescapedHostname[newPos]; if (!checkedAce) { checkedAce = true; if ((newPos + 3 < length) && (c == 'x') && IsIdnAce(unescapedHostname, newPos)) { foundAce = true; } } if (asciiLabel && (c > '\x7F')) { asciiLabel = false; allAscii = false; } if ((c == '.') || (c == '\u3002') || //IDEOGRAPHIC FULL STOP (c == '\uFF0E') || //FULLWIDTH FULL STOP (c == '\uFF61')) //HALFWIDTH IDEOGRAPHIC FULL STOP { foundDot = true; break; } ++newPos; } if (!asciiLabel) { string asciiForm = unescapedHostname.Substring(curPos, newPos - curPos); try { asciiForm = s_idnMapping.GetAscii(asciiForm); } catch (ArgumentException) { throw new UriFormatException(SR.net_uri_BadUnicodeHostForIdn); } unicodeEqvlHost += s_idnMapping.GetUnicode(asciiForm); if (foundDot) { unicodeEqvlHost += "."; } } else { bool aceValid = false; if (foundAce) { // check ace validity try { unicodeEqvlHost += s_idnMapping.GetUnicode(unescapedHostname, curPos, newPos - curPos); if (foundDot) { unicodeEqvlHost += "."; } aceValid = true; atLeastOneValidIdn = true; } catch (ArgumentException) { // not valid ace so treat it as a normal ascii label } } if (!aceValid) { // for invalid aces we just lowercase the label unicodeEqvlHost += unescapedHostname.Substring(curPos, newPos - curPos).ToLowerInvariant(); if (foundDot) { unicodeEqvlHost += "."; } } } curPos = newPos + (foundDot ? 1 : 0); } while (curPos < length); return(unicodeEqvlHost); }
internal static bool TryGetUnicodeEquivalent(string hostname, ref ValueStringBuilder dest) { Debug.Assert(ReferenceEquals(hostname, UriHelper.StripBidiControlCharacters(hostname, hostname))); int curPos = 0; // We run a loop where for every label // a) if label is ascii and no ace then we lowercase it // b) if label is ascii and ace and not valid idn then just lowercase it // c) if label is ascii and ace and is valid idn then get its unicode eqvl // d) if label is unicode then clean it by running it through idnmapping do { if (curPos != 0) { dest.Append('.'); } bool asciiLabel = true; //find the dot or hit the end int newPos; for (newPos = curPos; (uint)newPos < (uint)hostname.Length; newPos++) { char c = hostname[newPos]; if (c == '.') { break; } if (c > '\x7F') { asciiLabel = false; if ((c == '\u3002') || // IDEOGRAPHIC FULL STOP (c == '\uFF0E') || // FULLWIDTH FULL STOP (c == '\uFF61')) // HALFWIDTH IDEOGRAPHIC FULL STOP { break; } } } if (!asciiLabel) { try { string asciiForm = s_idnMapping.GetAscii(hostname, curPos, newPos - curPos); dest.Append(s_idnMapping.GetUnicode(asciiForm)); } catch (ArgumentException) { return(false); } } else { bool aceValid = false; if ((uint)(curPos + 3) < (uint)hostname.Length && hostname[curPos] == 'x' && hostname[curPos + 1] == 'n' && hostname[curPos + 2] == '-' && hostname[curPos + 3] == '-') { // check ace validity try { dest.Append(s_idnMapping.GetUnicode(hostname, curPos, newPos - curPos)); aceValid = true; } catch (ArgumentException) { // not valid ace so treat it as a normal ascii label } } if (!aceValid) { // for invalid aces we just lowercase the label ReadOnlySpan <char> slice = hostname.AsSpan(curPos, newPos - curPos); int charsWritten = slice.ToLowerInvariant(dest.AppendSpan(slice.Length)); Debug.Assert(charsWritten == slice.Length); } } curPos = newPos + 1; } while (curPos < hostname.Length); return(true); }