// constructor for outbound connections public MsQuicConnection(QuicClientConnectionOptions options) { ArgumentNullException.ThrowIfNull(options.RemoteEndPoint, nameof(options.RemoteEndPoint)); _remoteEndPoint = options.RemoteEndPoint; _configuration = SafeMsQuicConfigurationHandle.Create(options); _state.RemoteCertificateRequired = true; if (options.ClientAuthenticationOptions != null) { _state.RevocationMode = options.ClientAuthenticationOptions.CertificateRevocationCheckMode; _state.RemoteCertificateValidationCallback = options.ClientAuthenticationOptions.RemoteCertificateValidationCallback; _state.TargetHost = options.ClientAuthenticationOptions.TargetHost; } _state.StateGCHandle = GCHandle.Alloc(_state); try { Debug.Assert(!Monitor.IsEntered(_state), "!Monitor.IsEntered(_state)"); uint status = MsQuicApi.Api.ConnectionOpenDelegate( MsQuicApi.Api.Registration, s_connectionDelegate, GCHandle.ToIntPtr(_state.StateGCHandle), out _state.Handle); QuicExceptionHelpers.ThrowIfFailed(status, "Could not open the connection."); } catch { _state.StateGCHandle.Free(); throw; } _state.TraceId = MsQuicTraceHelper.GetTraceId(_state.Handle); if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info(_state, $"{TraceId()} Outbound connection created"); } }
private static unsafe uint NativeCallbackHandler( IntPtr listener, IntPtr context, ref ListenerEvent evt) { if (evt.Type != QUIC_LISTENER_EVENT.NEW_CONNECTION) { return(MsQuicStatusCodes.InternalError); } State state = (State)GCHandle.FromIntPtr(context).Target !; SafeMsQuicConnectionHandle?connectionHandle = null; try { ref NewConnectionInfo connectionInfo = ref *evt.Data.NewConnection.Info; IPEndPoint localEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(ref *(SOCKADDR_INET *)connectionInfo.LocalAddress); IPEndPoint remoteEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(ref *(SOCKADDR_INET *)connectionInfo.RemoteAddress); connectionHandle = new SafeMsQuicConnectionHandle(evt.Data.NewConnection.Connection); uint status = MsQuicApi.Api.ConnectionSetConfigurationDelegate(connectionHandle, state.ConnectionConfiguration); QuicExceptionHelpers.ThrowIfFailed(status, "ConnectionSetConfiguration failed."); var msQuicConnection = new MsQuicConnection(localEndPoint, remoteEndPoint, connectionHandle); msQuicConnection.SetNegotiatedAlpn(connectionInfo.NegotiatedAlpn, connectionInfo.NegotiatedAlpnLength); if (!state.AcceptConnectionQueue.Writer.TryWrite(msQuicConnection)) { // This handle will be cleaned up by MsQuic. connectionHandle.SetHandleAsInvalid(); msQuicConnection.Dispose(); return(MsQuicStatusCodes.InternalError); } return(MsQuicStatusCodes.Success); }
internal MsQuicListener(QuicListenerOptions options) { if (options.ListenEndPoint == null) { throw new ArgumentNullException(nameof(options.ListenEndPoint)); } _state = new State(options); _stateHandle = GCHandle.Alloc(_state); try { uint status = MsQuicApi.Api.ListenerOpenDelegate( MsQuicApi.Api.Registration, s_listenerDelegate, GCHandle.ToIntPtr(_stateHandle), out _state.Handle); QuicExceptionHelpers.ThrowIfFailed(status, "ListenerOpen failed."); } catch { _stateHandle.Free(); throw; } _state.TraceId = MsQuicTraceHelper.GetTraceId(_state.Handle); if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info(_state, $"{_state.TraceId} Listener created"); } _listenEndPoint = Start(options); if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info(_state, $"{_state.TraceId} Listener started"); } }
internal MsQuicListener(QuicListenerOptions options) { _state = new State(options); _stateHandle = GCHandle.Alloc(_state); try { uint status = MsQuicApi.Api.ListenerOpenDelegate( MsQuicApi.Api.Registration, s_listenerDelegate, GCHandle.ToIntPtr(_stateHandle), out _state.Handle); QuicExceptionHelpers.ThrowIfFailed(status, "ListenerOpen failed."); } catch { _state.Handle?.Dispose(); _stateHandle.Free(); throw; } _listenEndPoint = Start(options); }
// constructor for outbound connections public MsQuicConnection(QuicClientConnectionOptions options) { _remoteEndPoint = options.RemoteEndPoint !; _configuration = SafeMsQuicConfigurationHandle.Create(options); _isServer = false; _remoteCertificateRequired = true; if (options.ClientAuthenticationOptions != null) { _revocationMode = options.ClientAuthenticationOptions.CertificateRevocationCheckMode; _remoteCertificateValidationCallback = options.ClientAuthenticationOptions.RemoteCertificateValidationCallback; } _state.StateGCHandle = GCHandle.Alloc(_state); try { // this handle is ref counted by MsQuic, so safe to dispose here. using SafeMsQuicConfigurationHandle config = SafeMsQuicConfigurationHandle.Create(options); uint status = MsQuicApi.Api.ConnectionOpenDelegate( MsQuicApi.Api.Registration, s_connectionDelegate, GCHandle.ToIntPtr(_state.StateGCHandle), out _state.Handle); QuicExceptionHelpers.ThrowIfFailed(status, "Could not open the connection."); } catch { _state.StateGCHandle.Free(); throw; } if (NetEventSource.Log.IsEnabled()) { NetEventSource.Info(_state, $"[Connection#{_state.GetHashCode()}] outbound connection created"); } }
private unsafe IPEndPoint Start(QuicListenerOptions options) { List <SslApplicationProtocol> applicationProtocols = options.ServerAuthenticationOptions !.ApplicationProtocols !; IPEndPoint listenEndPoint = options.ListenEndPoint !; Internals.SocketAddress address = IPEndPointExtensions.Serialize(listenEndPoint); uint status; Debug.Assert(_stateHandle.IsAllocated); MemoryHandle[]? handles = null; QuicBuffer[]? buffers = null; try { Debug.Assert(!Monitor.IsEntered(_state), "!Monitor.IsEntered(_state)"); MsQuicAlpnHelper.Prepare(applicationProtocols, out handles, out buffers); fixed(byte *paddress = address.Buffer) { status = MsQuicApi.Api.ListenerStartDelegate(_state.Handle, (QuicBuffer *)Marshal.UnsafeAddrOfPinnedArrayElement(buffers, 0), (uint)applicationProtocols.Count, paddress); } } catch { _stateHandle.Free(); throw; } finally { MsQuicAlpnHelper.Return(ref handles, ref buffers); } QuicExceptionHelpers.ThrowIfFailed(status, "ListenerStart failed."); Debug.Assert(!Monitor.IsEntered(_state), "!Monitor.IsEntered(_state)"); return(MsQuicParameterHelpers.GetIPEndPointParam(MsQuicApi.Api, _state.Handle, (uint)QUIC_PARAM_LISTENER.LOCAL_ADDRESS)); }
internal MsQuicListener(QuicListenerOptions options) { _applicationProtocols = options.ServerAuthenticationOptions !.ApplicationProtocols !; _listenEndPoint = options.ListenEndPoint !; _state = new State(options); _stateHandle = GCHandle.Alloc(_state); try { uint status = MsQuicApi.Api.ListenerOpenDelegate( MsQuicApi.Api.Registration, s_listenerDelegate, GCHandle.ToIntPtr(_stateHandle), out _state.Handle); QuicExceptionHelpers.ThrowIfFailed(status, "ListenerOpen failed."); } catch { _state.Handle?.Dispose(); _stateHandle.Free(); throw; } }
internal override ValueTask ConnectAsync(CancellationToken cancellationToken = default) { ThrowIfDisposed(); if (_configuration is null) { throw new Exception($"{nameof(ConnectAsync)} must not be called on a connection obtained from a listener."); } QUIC_ADDRESS_FAMILY af = _remoteEndPoint.AddressFamily switch { AddressFamily.Unspecified => QUIC_ADDRESS_FAMILY.UNSPEC, AddressFamily.InterNetwork => QUIC_ADDRESS_FAMILY.INET, AddressFamily.InterNetworkV6 => QUIC_ADDRESS_FAMILY.INET6, _ => throw new Exception(SR.Format(SR.net_quic_unsupported_address_family, _remoteEndPoint.AddressFamily)) }; Debug.Assert(_state.StateGCHandle.IsAllocated); _state.Connection = this; uint status; string targetHost; int port; if (_remoteEndPoint is IPEndPoint) { SOCKADDR_INET address = MsQuicAddressHelpers.IPEndPointToINet((IPEndPoint)_remoteEndPoint); 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 *)&address); QuicExceptionHelpers.ThrowIfFailed(status, "Failed to connect to peer."); } targetHost = _state.TargetHost ?? ((IPEndPoint)_remoteEndPoint).Address.ToString(); port = ((IPEndPoint)_remoteEndPoint).Port; } 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; } else { throw new Exception($"Unsupported remote endpoint type '{_remoteEndPoint.GetType()}'."); } // We store TCS to local variable to avoid NRE if callbacks finish fast and set _state.ConnectTcs to null. var tcs = _state.ConnectTcs = new TaskCompletionSource <uint>(TaskCreationOptions.RunContinuationsAsynchronously); try { Debug.Assert(!Monitor.IsEntered(_state)); status = MsQuicApi.Api.ConnectionStartDelegate( _state.Handle, _configuration, af, targetHost, (ushort)port); QuicExceptionHelpers.ThrowIfFailed(status, "Failed to connect to peer."); // this handle is ref counted by MsQuic, so safe to dispose here. _configuration.Dispose(); _configuration = null; } catch { _state.StateGCHandle.Free(); _state.Connection = null; throw; } return(new ValueTask(tcs.Task)); }
internal override ValueTask ConnectAsync(CancellationToken cancellationToken = default) { ThrowIfDisposed(); if (_configuration is null) { throw new InvalidOperationException($"{nameof(ConnectAsync)} must not be called on a connection obtained from a listener."); } QUIC_ADDRESS_FAMILY af = _remoteEndPoint.AddressFamily switch { AddressFamily.Unspecified => QUIC_ADDRESS_FAMILY.UNSPEC, AddressFamily.InterNetwork => QUIC_ADDRESS_FAMILY.INET, AddressFamily.InterNetworkV6 => QUIC_ADDRESS_FAMILY.INET6, _ => throw new ArgumentException(SR.Format(SR.net_quic_unsupported_address_family, _remoteEndPoint.AddressFamily)) }; Debug.Assert(_state.StateGCHandle.IsAllocated); _state.Connection = this; uint status; string targetHost; int port; if (_remoteEndPoint is IPEndPoint ipEndPoint) { Debug.Assert(!Monitor.IsEntered(_state), "!Monitor.IsEntered(_state)"); MsQuicParameterHelpers.SetIPEndPointParam(MsQuicApi.Api, _state.Handle, (uint)QUIC_PARAM_CONN.REMOTE_ADDRESS, ipEndPoint); targetHost = _state.TargetHost ?? ((IPEndPoint)_remoteEndPoint).Address.ToString(); port = ((IPEndPoint)_remoteEndPoint).Port; } else if (_remoteEndPoint is DnsEndPoint dnsEndPoint) { port = dnsEndPoint.Port; string dnsHost = dnsEndPoint.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 Debug.Assert(!Monitor.IsEntered(_state), "!Monitor.IsEntered(_state)"); MsQuicParameterHelpers.SetIPEndPointParam(MsQuicApi.Api, _state.Handle, (uint)QUIC_PARAM_CONN.REMOTE_ADDRESS, new IPEndPoint(address, port)); targetHost = _state.TargetHost !; } else { targetHost = dnsHost; } } else { throw new ArgumentException($"Unsupported remote endpoint type '{_remoteEndPoint.GetType()}'."); } // We store TCS to local variable to avoid NRE if callbacks finish fast and set _state.ConnectTcs to null. var tcs = _state.ConnectTcs = new TaskCompletionSource <uint>(TaskCreationOptions.RunContinuationsAsynchronously); try { Debug.Assert(!Monitor.IsEntered(_state), "!Monitor.IsEntered(_state)"); status = MsQuicApi.Api.ConnectionStartDelegate( _state.Handle, _configuration, af, targetHost, (ushort)port); QuicExceptionHelpers.ThrowIfFailed(status, "Failed to connect to peer."); // this handle is ref counted by MsQuic, so safe to dispose here. _configuration.Dispose(); _configuration = null; } catch { _state.Connection = null; throw; } return(new ValueTask(tcs.Task)); }