Skip to content

Commit

Permalink
fix invalid Host header for link-local IPv6 (#69203)
Browse files Browse the repository at this point in the history
* fix invalid Host headr for link-local IPv6

* feedback from review
  • Loading branch information
wfurt authored May 12, 2022
1 parent e278502 commit e502177
Show file tree
Hide file tree
Showing 4 changed files with 26 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,12 @@ namespace System.Net.Http.Functional.Tests
#if WINHTTPHANDLER_TEST
using HttpClientHandler = System.Net.Http.WinHttpClientHandler;
#endif

// Note: Disposing the HttpClient object automatically disposes the handler within. So, it is not necessary
// to separately Dispose (or have a 'using' statement) for the handler.
public abstract class HttpClientHandlerTest : HttpClientHandlerTestBase
{
public static bool IsNotWinHttpHandler = !IsWinHttpHandler;

public HttpClientHandlerTest(ITestOutputHelper output) : base(output)
{
}
Expand Down Expand Up @@ -228,11 +229,12 @@ public static IEnumerable<object[]> GetAsync_IPBasedUri_Success_MemberData()
}
}

[Theory]
[InlineData("[::1234]")]
[InlineData("[::1234]:8080")]
[ConditionalTheory(nameof(IsNotWinHttpHandler))]
[InlineData("[::1234]", "[::1234]")]
[InlineData("[::1234]:8080", "[::1234]:8080")]
[InlineData("[fe80::9c3a:b64d:6249:1de8%2]", "[fe80::9c3a:b64d:6249:1de8]")]
[SkipOnPlatform(TestPlatforms.Browser, "Proxy not supported on Browser")]
public async Task GetAsync_IPv6AddressInHostHeader_CorrectlyFormatted(string host)
public async Task GetAsync_IPv6AddressInHostHeader_CorrectlyFormatted(string host, string hostHeader)
{
string ipv6Address = "http://" + host;
bool connectionAccepted = false;
Expand Down Expand Up @@ -261,7 +263,7 @@ await LoopbackServer.CreateClientAndServerAsync(async proxyUri =>
{
connectionAccepted = true;
List<string> headers = await connection.ReadRequestHeaderAndSendResponseAsync();
Assert.Contains($"Host: {host}", headers);
Assert.Contains($"Host: {hostHeader}", headers);
}));

Assert.True(connectionAccepted);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ internal sealed class HttpAuthority : IEquatable<HttpAuthority>
// public string AlpnProtocolName { get; }

public string IdnHost { get; }
public string HostValue { get; }
public int Port { get; }

public HttpAuthority(string host, int port)
Expand All @@ -23,10 +24,18 @@ public HttpAuthority(string host, int port)
var builder = new UriBuilder(Uri.UriSchemeHttp, host, port);
Uri uri = builder.Uri;

// TODO https://github.com/dotnet/runtime/issues/25782:
// Uri.IdnHost is missing '[', ']' characters around IPv6 address.
// So, we need to add them manually for now.
IdnHost = uri.HostNameType == UriHostNameType.IPv6 ? "[" + uri.IdnHost + "]" : uri.IdnHost;
if (uri.HostNameType == UriHostNameType.IPv6)
{
// This includes brackets for IPv6 and ScopeId for IPv6 LLA so Connect works.
IdnHost = $"[{uri.IdnHost}]";
// This is bracket enclosed IPv6 without ScopeID for LLA
HostValue = uri.Host;
}
else
{
// IPv4 address, dns or puny encoded name
HostValue = IdnHost = uri.IdnHost;
}
Port = port;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -334,14 +334,11 @@ private async ValueTask WriteHostHeaderAsync(Uri uri, bool async)
{
Debug.Assert(Kind == HttpConnectionKind.Proxy);

// TODO https://github.com/dotnet/runtime/issues/25782:
// Uri.IdnHost is missing '[', ']' characters around IPv6 address.
// So, we need to add them manually for now.
// Uri.IdnHost is missing '[', ']' characters around IPv6 address
// and it also contains ScopeID for Link-Local addresses
if (uri.HostNameType == UriHostNameType.IPv6)
{
await WriteByteAsync((byte)'[', async).ConfigureAwait(false);
await WriteAsciiStringAsync(uri.IdnHost, async).ConfigureAwait(false);
await WriteByteAsync((byte)']', async).ConfigureAwait(false);
await WriteAsciiStringAsync(uri.Host, async).ConfigureAwait(false);
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,8 @@ public HttpConnectionPool(HttpConnectionPoolManager poolManager, HttpConnectionK
// Note that if _host is null, this is a (non-tunneled) proxy connection, and we can't cache the hostname.
hostHeader =
(_originAuthority.Port != (sslHostName == null ? DefaultHttpPort : DefaultHttpsPort)) ?
$"{_originAuthority.IdnHost}:{_originAuthority.Port}" :
_originAuthority.IdnHost;
$"{_originAuthority.HostValue}:{_originAuthority.Port}" :
_originAuthority.HostValue;

// Note the IDN hostname should always be ASCII, since it's already been IDNA encoded.
_hostHeaderValueBytes = Encoding.ASCII.GetBytes(hostHeader);
Expand Down

0 comments on commit e502177

Please sign in to comment.