fix(quic): handle Linux dual-stack listeners [CI]
Signed-off-by: JustMe <mr.toing@outlook.com>
This commit is contained in:
1 parent
b9ec6d80b0
commit
b5e275c51f
2 files changed
+50
-14
No files matched your search
@@ -346,11 +346,22 @@ internal sealed class MsQuicConnection : IAsyncDisposable, IDatagramConnection
|
||||
connection._connected.TrySetResult();
|
||||
break;
|
||||
case ConnectionEventShutdownByTransport:
|
||||
case ConnectionEventShutdownByPeer:
|
||||
connection._connected.TrySetException(new IOException("QUIC connection closed during handshake."));
|
||||
{
|
||||
uint status = unchecked((uint)Marshal.ReadInt32(eventPointer, IntPtr.Size));
|
||||
ulong errorCode = unchecked((ulong)Marshal.ReadInt64(eventPointer, IntPtr.Size * 2));
|
||||
connection._log.Error("MsQuic QUIC transport shutdown", ("status", $"0x{status:X8}"), ("error_code", errorCode));
|
||||
connection._connected.TrySetException(new IOException($"QUIC connection closed during handshake (transport status 0x{status:X8}, error code {errorCode})."));
|
||||
break;
|
||||
}
|
||||
case ConnectionEventShutdownByPeer:
|
||||
{
|
||||
ulong errorCode = unchecked((ulong)Marshal.ReadInt64(eventPointer, IntPtr.Size));
|
||||
connection._log.Warn("MsQuic QUIC peer shutdown", ("error_code", errorCode));
|
||||
connection._connected.TrySetException(new IOException($"QUIC connection closed during handshake by peer (error code {errorCode})."));
|
||||
break;
|
||||
}
|
||||
case ConnectionEventShutdownComplete:
|
||||
connection.OnShutdownComplete(handle);
|
||||
connection.OnShutdownComplete(handle, eventPointer);
|
||||
break;
|
||||
case ConnectionEventPeerAddressChanged:
|
||||
{
|
||||
@@ -467,7 +478,14 @@ internal sealed class MsQuicConnection : IAsyncDisposable, IDatagramConnection
|
||||
}
|
||||
|
||||
using X509Certificate2 peerCertificate = X509CertificateLoader.LoadCertificate(der);
|
||||
bool valid = ExportSubjectPublicKeyInfo(peerCertificate).AsSpan().SequenceEqual(_expectedPeerSpki);
|
||||
byte[] actualSpki = ExportSubjectPublicKeyInfo(peerCertificate);
|
||||
bool valid = actualSpki.AsSpan().SequenceEqual(_expectedPeerSpki);
|
||||
if (!valid)
|
||||
{
|
||||
_log.Error("MsQuic peer certificate SPKI mismatch",
|
||||
("expected_sha256", Convert.ToHexStringLower(SHA256.HashData(_expectedPeerSpki))),
|
||||
("actual_sha256", Convert.ToHexStringLower(SHA256.HashData(actualSpki))));
|
||||
}
|
||||
return valid ? 0 : OperatingSystem.IsWindows() ? 0x80004005u : 1u;
|
||||
}
|
||||
|
||||
@@ -513,8 +531,16 @@ internal sealed class MsQuicConnection : IAsyncDisposable, IDatagramConnection
|
||||
state.Release();
|
||||
}
|
||||
|
||||
private void OnShutdownComplete(nint handle)
|
||||
private void OnShutdownComplete(nint handle, nint eventPointer)
|
||||
{
|
||||
if (!_connected.Task.IsCompletedSuccessfully)
|
||||
{
|
||||
byte flags = Marshal.ReadByte(eventPointer, IntPtr.Size);
|
||||
_log.Warn("MsQuic shutdown completed before handshake notification",
|
||||
("handshake_completed", (flags & 1) != 0),
|
||||
("peer_acknowledged", (flags & 2) != 0));
|
||||
}
|
||||
|
||||
if (Interlocked.Exchange(ref _handle, 0) == 0)
|
||||
{
|
||||
return;
|
||||
|
||||
@@ -15,7 +15,7 @@ internal sealed class MsQuicListener : IAsyncDisposable
|
||||
private const uint ListenerLocalAddress = 0x04000000;
|
||||
private static readonly MsQuicApi.ListenerCallback s_callback = OnListenerEvent;
|
||||
|
||||
private readonly IPAddress _bindAddress;
|
||||
private readonly IReadOnlyList<IPAddress> _bindAddresses;
|
||||
private readonly IReadOnlyList<int> _ports;
|
||||
private readonly MsQuicDatagramCallback _datagramHandler;
|
||||
private readonly INodeLog _log;
|
||||
@@ -36,7 +36,12 @@ internal sealed class MsQuicListener : IAsyncDisposable
|
||||
INodeLog log,
|
||||
Action<IPEndPoint>? peerAddressChanged)
|
||||
{
|
||||
_bindAddress = bindAddress;
|
||||
// MsQuic opens dual-mode wildcard sockets, but filters incoming connections using
|
||||
// the address family supplied to ListenerStart. An IPv6Any listener alone rejects
|
||||
// IPv4-mapped connections with TLS alert 120 (no_application_protocol).
|
||||
_bindAddresses = OperatingSystem.IsLinux() && bindAddress.Equals(IPAddress.IPv6Any)
|
||||
? [IPAddress.Any, IPAddress.IPv6Any]
|
||||
: [bindAddress];
|
||||
_ports = ports.Count == 0 ? [7443] : ports;
|
||||
_certificate = certificate;
|
||||
_datagramHandler = datagramHandler;
|
||||
@@ -59,17 +64,22 @@ internal sealed class MsQuicListener : IAsyncDisposable
|
||||
{
|
||||
_configuration = MsQuicApi.ConfigurationOpen(_registration, QuicAlpn.Value);
|
||||
MsQuicConnection.LoadServerCredentials(_configuration, _certificate);
|
||||
var bound = new List<int>(_ports.Count);
|
||||
var bound = new List<int>(_ports.Count * _bindAddresses.Count);
|
||||
foreach (int port in _ports)
|
||||
{
|
||||
nint listener = MsQuicApi.ListenerOpen(_registration, s_callback, GCHandle.ToIntPtr(_self));
|
||||
_listeners.Add(listener);
|
||||
byte[] address = Serialize(new IPEndPoint(_bindAddress, port));
|
||||
MsQuicApi.ListenerStart(listener, QuicAlpn.Value, address);
|
||||
bound.Add(ReadPort(MsQuicApi.GetAddress(listener, ListenerLocalAddress)));
|
||||
int effectivePort = port;
|
||||
foreach (IPAddress bindAddress in _bindAddresses)
|
||||
{
|
||||
nint listener = MsQuicApi.ListenerOpen(_registration, s_callback, GCHandle.ToIntPtr(_self));
|
||||
_listeners.Add(listener);
|
||||
byte[] address = Serialize(new IPEndPoint(bindAddress, effectivePort));
|
||||
MsQuicApi.ListenerStart(listener, QuicAlpn.Value, address);
|
||||
effectivePort = ReadPort(MsQuicApi.GetAddress(listener, ListenerLocalAddress));
|
||||
bound.Add(effectivePort);
|
||||
}
|
||||
}
|
||||
|
||||
BoundPorts = bound;
|
||||
BoundPorts = bound.Distinct().ToArray();
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
Reference in new issue
Block a user