Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve SNI handling when IP is passed in as DnsEndPoint to QuicListener #57255

Merged
merged 1 commit into from
Aug 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -599,9 +599,28 @@ internal override ValueTask ConnectAsync(CancellationToken cancellationToken = d
}
else if (_remoteEndPoint is DnsEndPoint)
{
// We don't have way how to set separate SNI and name for connection at this moment.
targetHost = ((DnsEndPoint)_remoteEndPoint).Host;
port = ((DnsEndPoint)_remoteEndPoint).Port;
string dnsHost = ((DnsEndPoint)_remoteEndPoint).Host!;

// We don't have way how to set separate SNI and name for connection at this moment.
// If the name is actually IP address we can use it to make at least some cases work for people
// who want to bypass DNS but connect to specific virtual host.
if (!string.IsNullOrEmpty(_state.TargetHost) && !dnsHost.Equals(_state.TargetHost, StringComparison.InvariantCultureIgnoreCase) && IPAddress.TryParse(dnsHost, out IPAddress? address))
{
// This is form of IPAddress and _state.TargetHost is set to different string
SOCKADDR_INET quicAddress = MsQuicAddressHelpers.IPEndPointToINet(new IPEndPoint(address, port));
unsafe
{
Debug.Assert(!Monitor.IsEntered(_state));
status = MsQuicApi.Api.SetParamDelegate(_state.Handle, QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.REMOTE_ADDRESS, (uint)sizeof(SOCKADDR_INET), (byte*)&quicAddress);
QuicExceptionHelpers.ThrowIfFailed(status, "Failed to connect to peer.");
}
targetHost = _state.TargetHost!;
}
else
{
targetHost = dnsHost;
}
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ public State(QuicListenerOptions options)

internal MsQuicListener(QuicListenerOptions options)
{
if (options.ListenEndPoint == null)
{
throw new ArgumentNullException(nameof(options.ListenEndPoint));
}

_state = new State(options);
_stateHandle = GCHandle.Alloc(_state);
try
Expand Down
27 changes: 27 additions & 0 deletions src/libraries/System.Net.Quic/tests/FunctionalTests/MsQuicTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,33 @@ public async Task ConnectWithCertificateCallback()
serverConnection.Dispose();
}

[Fact]
public async Task ConnectWithIpSetsSni()
{
X509Certificate2 certificate = System.Net.Test.Common.Configuration.Certificates.GetServerCertificate();
string expectedName = "foobar";
string? receivedHostName = null;

var listenerOptions = CreateQuicListenerOptions();
listenerOptions.ServerAuthenticationOptions.ServerCertificate = null;
listenerOptions.ServerAuthenticationOptions.ServerCertificateSelectionCallback = (sender, hostName) =>
{
receivedHostName = hostName;
return certificate;
};

using QuicListener listener = new QuicListener(QuicImplementationProviders.MsQuic, listenerOptions);

QuicClientConnectionOptions clientOptions = CreateQuicClientOptions();
clientOptions.ClientAuthenticationOptions.TargetHost = expectedName;
clientOptions.RemoteEndPoint = new DnsEndPoint("127.0.0.1", listener.ListenEndPoint.Port);

(QuicConnection clientConnection, QuicConnection serverConnection) = await CreateConnectedQuicConnection(clientOptions, listener);
Assert.Equal(expectedName, receivedHostName);
clientConnection.Dispose();
serverConnection.Dispose();
}

[Fact]
public async Task ConnectWithCertificateForDifferentName_Throws()
{
Expand Down