示例#1
0
        // constructor for outbound connections
        public MsQuicConnection(QuicClientConnectionOptions options)
        {
            _remoteEndPoint = options.RemoteEndPoint !;
            _configuration  = SafeMsQuicConfigurationHandle.Create(options);

            _stateHandle = 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(_stateHandle),
                    out _state.Handle);

                QuicExceptionHelpers.ThrowIfFailed(status, "Could not open the connection.");
            }
            catch
            {
                _stateHandle.Free();
                throw;
            }
        }
        internal override ValueTask ConnectAsync(CancellationToken cancellationToken = default)
        {
            ThrowIfDisposed();

            (string address, int port) = _remoteEndPoint switch
            {
                DnsEndPoint dnsEp => (dnsEp.Host, dnsEp.Port),
                IPEndPoint ipEp => (ipEp.Address.ToString(), ipEp.Port),
                _ => throw new Exception($"Unsupported remote endpoint type '{_remoteEndPoint.GetType()}'.")
            };

            // values taken from https://github.com/microsoft/msquic/blob/main/docs/api/ConnectionStart.md
            int af = _remoteEndPoint.AddressFamily switch
            {
                AddressFamily.Unspecified => 0,
                AddressFamily.InterNetwork => 2,
                AddressFamily.InterNetworkV6 => 23,
                _ => throw new Exception(SR.Format(SR.net_quic_unsupported_address_family, _remoteEndPoint.AddressFamily))
            };

            QuicExceptionHelpers.ThrowIfFailed(
                MsQuicApi.Api.ConnectionStartDelegate(
                    _ptr,
                    (ushort)af,
                    address,
                    (ushort)port),
                "Failed to connect to peer.");

            return(new ValueTask(_connectTcs.Task));
        }
        private MsQuicStream StreamOpen(
            QUIC_STREAM_OPEN_FLAG flags)
        {
            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Enter(this);
            }

            IntPtr streamPtr = IntPtr.Zero;

            QuicExceptionHelpers.ThrowIfFailed(
                MsQuicApi.Api.StreamOpenDelegate(
                    _ptr,
                    (uint)flags,
                    MsQuicStream.NativeCallbackHandler,
                    IntPtr.Zero,
                    out streamPtr),
                "Failed to open stream to peer.");

            MsQuicStream stream = new MsQuicStream(this, flags, streamPtr, inbound: false);

            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Exit(this);
            }
            return(stream);
        }
示例#4
0
        private unsafe IPEndPoint Start(QuicListenerOptions options)
        {
            List <SslApplicationProtocol> applicationProtocols = options.ServerAuthenticationOptions !.ApplicationProtocols !;
            IPEndPoint listenEndPoint = options.ListenEndPoint !;

            SOCKADDR_INET address = MsQuicAddressHelpers.IPEndPointToINet(listenEndPoint);

            uint status;

            Debug.Assert(_stateHandle.IsAllocated);

            MemoryHandle[]? handles = null;
            QuicBuffer[]? buffers   = null;
            try
            {
                MsQuicAlpnHelper.Prepare(applicationProtocols, out handles, out buffers);
                status = MsQuicApi.Api.ListenerStartDelegate(_state.Handle, (QuicBuffer *)Marshal.UnsafeAddrOfPinnedArrayElement(buffers, 0), (uint)applicationProtocols.Count, ref address);
            }
            catch
            {
                _stateHandle.Free();
                throw;
            }
            finally
            {
                MsQuicAlpnHelper.Return(ref handles, ref buffers);
            }

            QuicExceptionHelpers.ThrowIfFailed(status, "ListenerStart failed.");

            SOCKADDR_INET inetAddress = MsQuicParameterHelpers.GetINetParam(MsQuicApi.Api, _state.Handle, QUIC_PARAM_LEVEL.LISTENER, (uint)QUIC_PARAM_LISTENER.LOCAL_ADDRESS);

            return(MsQuicAddressHelpers.INetToIPEndPoint(ref inetAddress));
        }
示例#5
0
        // 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
            {
                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");
            }
        }
示例#6
0
        // outbound.
        internal MsQuicStream(SafeMsQuicConnectionHandle connection, QUIC_STREAM_OPEN_FLAGS flags)
        {
            Debug.Assert(connection != null);

            _canRead  = !flags.HasFlag(QUIC_STREAM_OPEN_FLAGS.UNIDIRECTIONAL);
            _canWrite = true;

            _stateHandle = GCHandle.Alloc(_state);
            try
            {
                uint status = MsQuicApi.Api.StreamOpenDelegate(
                    connection,
                    flags,
                    s_streamDelegate,
                    GCHandle.ToIntPtr(_stateHandle),
                    out _state.Handle);

                QuicExceptionHelpers.ThrowIfFailed(status, "Failed to open stream to peer.");

                status = MsQuicApi.Api.StreamStartDelegate(_state.Handle, QUIC_STREAM_START_FLAGS.ASYNC);
                QuicExceptionHelpers.ThrowIfFailed(status, "Could not start stream.");
            }
            catch
            {
                _state.Handle?.Dispose();
                _stateHandle.Free();
                throw;
            }
        }
示例#7
0
        internal override unsafe void Start()
        {
            ThrowIfDisposed();

            SOCKADDR_INET address = MsQuicAddressHelpers.IPEndPointToINet(_listenEndPoint);

            uint status;

            MemoryHandle[]? handles = null;
            QuicBuffer[]? buffers   = null;
            try
            {
                MsQuicAlpnHelper.Prepare(_applicationProtocols, out handles, out buffers);
                status = MsQuicApi.Api.ListenerStartDelegate(_state.Handle, (QuicBuffer *)Marshal.UnsafeAddrOfPinnedArrayElement(buffers, 0), (uint)_applicationProtocols.Count, ref address);
            }
            finally
            {
                MsQuicAlpnHelper.Return(ref handles, ref buffers);
            }

            QuicExceptionHelpers.ThrowIfFailed(status, "ListenerStart failed.");

            SOCKADDR_INET inetAddress = MsQuicParameterHelpers.GetINetParam(MsQuicApi.Api, _state.Handle, QUIC_PARAM_LEVEL.LISTENER, (uint)QUIC_PARAM_LISTENER.LOCAL_ADDRESS);

            _listenEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(ref inetAddress);
        }
        // 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;
            }

            _stateHandle = 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(_stateHandle),
                    out _state.Handle);

                QuicExceptionHelpers.ThrowIfFailed(status, "Could not open the connection.");
            }
            catch
            {
                _stateHandle.Free();
                throw;
            }
        }
示例#9
0
        internal MsQuicListener(QuicListenerOptions options)
        {
            ArgumentNullException.ThrowIfNull(options.ListenEndPoint, 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");
            }
        }
示例#10
0
 private void SetParam(
     QUIC_PARAM_SESSION param,
     MsQuicNativeMethods.QuicBuffer buf)
 {
     QuicExceptionHelpers.ThrowIfFailed(MsQuicApi.Api.UnsafeSetParam(
                                            _nativeObjPtr,
                                            (uint)QUIC_PARAM_LEVEL.SESSION,
                                            (uint)param,
                                            buf),
                                        "Could not set parameter on session.");
 }
示例#11
0
        private ValueTask ShutdownAsync(
            QUIC_CONNECTION_SHUTDOWN_FLAG Flags,
            long ErrorCode)
        {
            uint status = MsQuicApi.Api.ConnectionShutdownDelegate(
                _ptr,
                (uint)Flags,
                ErrorCode);

            QuicExceptionHelpers.ThrowIfFailed(status, "Failed to shutdown connection.");

            return(new ValueTask(_shutdownTcs.Task));
        }
示例#12
0
        // outbound.
        internal MsQuicStream(MsQuicConnection.State connectionState, QUIC_STREAM_OPEN_FLAGS flags)
        {
            Debug.Assert(connectionState.Handle != null);

            _canRead  = !flags.HasFlag(QUIC_STREAM_OPEN_FLAGS.UNIDIRECTIONAL);
            _canWrite = true;

            _state.StateGCHandle = GCHandle.Alloc(_state);
            if (!_canRead)
            {
                _state.ReadState = ReadState.Closed;
            }

            try
            {
                uint status = MsQuicApi.Api.StreamOpenDelegate(
                    connectionState.Handle,
                    flags,
                    s_streamDelegate,
                    GCHandle.ToIntPtr(_state.StateGCHandle),
                    out _state.Handle);

                QuicExceptionHelpers.ThrowIfFailed(status, "Failed to open stream to peer.");

                status = MsQuicApi.Api.StreamStartDelegate(_state.Handle, QUIC_STREAM_START_FLAGS.FAIL_BLOCKED);
                QuicExceptionHelpers.ThrowIfFailed(status, "Could not start stream.");
            }
            catch
            {
                _state.Handle?.Dispose();
                _state.StateGCHandle.Free();
                throw;
            }

            if (!connectionState.TryAddStream(this))
            {
                _state.Handle?.Dispose();
                _state.StateGCHandle.Free();
                throw new ObjectDisposedException(nameof(QuicConnection));
            }

            _state.ConnectionState = connectionState;

            if (NetEventSource.Log.IsEnabled())
            {
                NetEventSource.Info(
                    _state,
                    $"[Stream#{_state.GetHashCode()}] outbound {(flags.HasFlag(QUIC_STREAM_OPEN_FLAGS.UNIDIRECTIONAL) ? "uni" : "bi")}directional stream created " +
                    $"in Connection#{_state.ConnectionState.GetHashCode()}.");
            }
        }
示例#13
0
        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.");
            }

            (string address, int port) = _remoteEndPoint switch
            {
                DnsEndPoint dnsEp => (dnsEp.Host, dnsEp.Port),
                IPEndPoint ipEp => (ipEp.Address.ToString(), ipEp.Port),
                _ => throw new Exception($"Unsupported remote endpoint type '{_remoteEndPoint.GetType()}'.")
            };

            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;
            try
            {
                uint status = MsQuicApi.Api.ConnectionStartDelegate(
                    _state.Handle,
                    _configuration,
                    af,
                    address,
                    (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(_state.ConnectTcs.Task));
        }
        internal override ValueTask ConnectAsync(CancellationToken cancellationToken = default)
        {
            ThrowIfDisposed();

            QuicExceptionHelpers.ThrowIfFailed(
                MsQuicApi.Api.ConnectionStartDelegate(
                    _ptr,
                    (ushort)_remoteEndPoint.AddressFamily,
                    _remoteEndPoint.Address.ToString(),
                    (ushort)_remoteEndPoint.Port),
                "Failed to connect to peer.");

            return(_connectTcs.GetTypelessValueTask());
        }
示例#15
0
        internal override void Start()
        {
            ThrowIfDisposed();

            SetCallbackHandler();

            SOCKADDR_INET address = MsQuicAddressHelpers.IPEndPointToINet(_listenEndPoint);

            QuicExceptionHelpers.ThrowIfFailed(MsQuicApi.Api.ListenerStartDelegate(
                                                   _ptr,
                                                   ref address),
                                               "Failed to start listener.");

            SetListenPort();
        }
示例#16
0
        private MsQuicStream StreamOpen(
            QUIC_STREAM_OPEN_FLAG flags)
        {
            IntPtr streamPtr = IntPtr.Zero;

            QuicExceptionHelpers.ThrowIfFailed(
                MsQuicApi.Api.StreamOpenDelegate(
                    _ptr,
                    (uint)flags,
                    MsQuicStream.s_streamDelegate,
                    IntPtr.Zero,
                    out streamPtr),
                "Failed to open stream to peer.");

            return(new MsQuicStream(this, flags, streamPtr, inbound: false));
        }
示例#17
0
        private static unsafe uint NativeCallbackHandler(
            IntPtr listener,
            IntPtr context,
            ref ListenerEvent evt)
        {
            if (evt.Type != QUIC_LISTENER_EVENT.NEW_CONNECTION)
            {
                return(MsQuicStatusCodes.InternalError);
            }

            GCHandle gcHandle = GCHandle.FromIntPtr(context);

            Debug.Assert(gcHandle.IsAllocated);
            Debug.Assert(gcHandle.Target is not null);
            var state = (State)gcHandle.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, state.RemoteCertificateRequired, state.RevocationMode, state.RemoteCertificateValidationCallback);
                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);
            }
        private ValueTask ShutdownAsync(
            QUIC_CONNECTION_SHUTDOWN_FLAG Flags,
            long ErrorCode)
        {
            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Enter(this);
            }

            uint status = MsQuicApi.Api.ConnectionShutdownDelegate(
                _ptr,
                (uint)Flags,
                ErrorCode);

            QuicExceptionHelpers.ThrowIfFailed(status, "Failed to shutdown connection.");

            if (NetEventSource.IsEnabled)
            {
                NetEventSource.Exit(this);
            }
            return(_shutdownTcs.GetTypelessValueTask());
        }
示例#19
0
        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
            {
                _stateHandle.Free();
                throw;
            }

            _listenEndPoint = Start(options);
        }
示例#20
0
        internal override void Start()
        {
            ThrowIfDisposed();

            // protect against double starts.
            if (_started)
            {
                throw new QuicException("Cannot start Listener multiple times");
            }

            _started = true;
            SetCallbackHandler();

            SOCKADDR_INET address = MsQuicAddressHelpers.IPEndPointToINet(_listenEndPoint);

            QuicExceptionHelpers.ThrowIfFailed(MsQuicApi.Api.ListenerStartDelegate(
                                                   _ptr,
                                                   ref address),
                                               "Failed to start listener.");

            SetListenPort();
        }
示例#21
0
        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));
        }
示例#22
0
        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;
            }
        }
示例#23
0
        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));
        }
示例#24
0
        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));
        }