Beispiel #1
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));
        }
Beispiel #2
0
        internal async ValueTask SetSecurityConfigForConnection(X509Certificate cert)
        {
            _securityConfig = await MsQuicApi.Api.CreateSecurityConfig(cert);

            // TODO this isn't being set correctly
            MsQuicParameterHelpers.SetSecurityConfig(MsQuicApi.Api, _ptr, (uint)QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.SEC_CONFIG, _securityConfig.NativeObjPtr);
        }
Beispiel #3
0
        internal async ValueTask SetSecurityConfigForConnection(X509Certificate cert, string?certFilePath, string?privateKeyFilePath)
        {
            _securityConfig = await MsQuicApi.Api.CreateSecurityConfig(cert, certFilePath, privateKeyFilePath).ConfigureAwait(false);

            // TODO this isn't being set correctly
            MsQuicParameterHelpers.SetSecurityConfig(MsQuicApi.Api, _ptr, (uint)QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.SEC_CONFIG, _securityConfig !.NativeObjPtr);
        }
Beispiel #4
0
        private uint HandleEventConnected(ConnectionEvent connectionEvent)
        {
            SOCKADDR_INET inetAddress = MsQuicParameterHelpers.GetINetParam(MsQuicApi.Api, _ptr, (uint)QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.LOCAL_ADDRESS);

            _localEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(inetAddress);

            _connected = true;
            // I don't believe we need to lock here because
            // handle event connected will not be called at the same time as
            // handle event shutdown initiated by transport
            _connectTcs.Complete(MsQuicStatusCodes.Success);

            return(MsQuicStatusCodes.Success);
        }
Beispiel #5
0
        private static uint HandleEventConnected(State state, ref ConnectionEvent connectionEvent)
        {
            if (state.Connected)
            {
                return(MsQuicStatusCodes.Success);
            }

            if (state.IsServer)
            {
                state.Connected = true;
                MsQuicListener.State?listenerState = state.ListenerState;
                state.ListenerState = null;

                if (listenerState != null)
                {
                    if (listenerState.PendingConnections.TryRemove(state.Handle.DangerousGetHandle(), out MsQuicConnection? connection))
                    {
                        // Move connection from pending to Accept queue and hand it out.
                        if (listenerState.AcceptConnectionQueue.Writer.TryWrite(connection))
                        {
                            return(MsQuicStatusCodes.Success);
                        }
                        // Listener is closed
                        connection.Dispose();
                    }
                }

                return(MsQuicStatusCodes.UserCanceled);
            }
            else
            {
                // Connected will already be true for connections accepted from a listener.
                Debug.Assert(!Monitor.IsEntered(state));


                Debug.Assert(state.Connection != null);
                state.Connection._localEndPoint = MsQuicParameterHelpers.GetIPEndPointParam(MsQuicApi.Api, state.Handle, (uint)QUIC_PARAM_CONN.LOCAL_ADDRESS);
                state.Connection.SetNegotiatedAlpn(connectionEvent.Data.Connected.NegotiatedAlpn, connectionEvent.Data.Connected.NegotiatedAlpnLength);
                state.Connection = null;

                state.Connected = true;
                state.ConnectTcs !.SetResult(MsQuicStatusCodes.Success);
                state.ConnectTcs = null;
            }

            return(MsQuicStatusCodes.Success);
        }
Beispiel #6
0
        private static unsafe int HandleEventConnected(State state, ref QUIC_CONNECTION_EVENT connectionEvent)
        {
            if (state.Connected)
            {
                return(QUIC_STATUS_SUCCESS);
            }

            if (state.IsServer)
            {
                state.Connected = true;
                MsQuicListener.State?listenerState = state.ListenerState;
                state.ListenerState = null;

                if (listenerState != null)
                {
                    if (listenerState.PendingConnections.TryRemove(state.Handle.DangerousGetHandle(), out MsQuicConnection? connection))
                    {
                        // Move connection from pending to Accept queue and hand it out.
                        if (listenerState.AcceptConnectionQueue.Writer.TryWrite(connection))
                        {
                            return(QUIC_STATUS_SUCCESS);
                        }
                        // Listener is closed
                        connection.Dispose();
                    }
                }

                return(QUIC_STATUS_USER_CANCELED);
            }
            else
            {
                // Connected will already be true for connections accepted from a listener.
                Debug.Assert(!Monitor.IsEntered(state));


                Debug.Assert(state.Connection != null);
                state.Connection._localEndPoint = MsQuicParameterHelpers.GetIPEndPointParam(MsQuicApi.Api, state.Handle, QUIC_PARAM_CONN_LOCAL_ADDRESS);
                state.Connection.SetNegotiatedAlpn((IntPtr)connectionEvent.CONNECTED.NegotiatedAlpn, connectionEvent.CONNECTED.NegotiatedAlpnLength);
                state.Connection = null;

                state.Connected = true;
                state.ConnectTcs !.SetResult(QUIC_STATUS_SUCCESS);
                state.ConnectTcs = null;
            }

            return(QUIC_STATUS_SUCCESS);
        }
Beispiel #7
0
        private uint HandleEventConnected(ref ConnectionEvent connectionEvent)
        {
            if (!_connected)
            {
                // _connected will already be true for connections accepted from a listener.

                SOCKADDR_INET inetAddress = MsQuicParameterHelpers.GetINetParam(MsQuicApi.Api, _ptr, (uint)QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.LOCAL_ADDRESS);
                _localEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(ref inetAddress);

                SetNegotiatedAlpn(connectionEvent.Data.Connected.NegotiatedAlpn, connectionEvent.Data.Connected.NegotiatedAlpnLength);

                _connected = true;
                _connectTcs.SetResult(MsQuicStatusCodes.Success);
            }

            return(MsQuicStatusCodes.Success);
        }
Beispiel #8
0
        private static uint HandleEventConnected(State state, ref ConnectionEvent connectionEvent)
        {
            if (!state.Connected)
            {
                // Connected will already be true for connections accepted from a listener.
                Debug.Assert(!Monitor.IsEntered(state));
                SOCKADDR_INET inetAddress = MsQuicParameterHelpers.GetINetParam(MsQuicApi.Api, state.Handle, QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.LOCAL_ADDRESS);

                Debug.Assert(state.Connection != null);
                state.Connection._localEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(ref inetAddress);
                state.Connection.SetNegotiatedAlpn(connectionEvent.Data.Connected.NegotiatedAlpn, connectionEvent.Data.Connected.NegotiatedAlpnLength);
                state.Connection = null;

                state.Connected = true;
                state.ConnectTcs !.SetResult(MsQuicStatusCodes.Success);
                state.ConnectTcs = null;
            }

            return(MsQuicStatusCodes.Success);
        }
Beispiel #9
0
        private unsafe IPEndPoint Start(QuicListenerOptions options)
        {
            List <SslApplicationProtocol> applicationProtocols = options.ServerAuthenticationOptions !.ApplicationProtocols !;
            IPEndPoint listenEndPoint = options.ListenEndPoint !;

            Debug.Assert(_stateHandle.IsAllocated);
            try
            {
                Debug.Assert(!Monitor.IsEntered(_state), "!Monitor.IsEntered(_state)");
                using var msquicBuffers = new MsQuicBuffers();
                msquicBuffers.Initialize(applicationProtocols, applicationProtocol => applicationProtocol.Protocol);

                QuicAddr address = listenEndPoint.ToQuicAddr();

                if (listenEndPoint.Address.Equals(IPAddress.IPv6Any))
                {
                    // For IPv6Any, MsQuic would listen only for IPv6 connections. This would make it impossible
                    // to connect the listener by using the IPv4 address (which could have been e.g. resolved by DNS).
                    // Using the Unspecified family makes MsQuic handle connections from all IP addresses.
                    address.Family = QUIC_ADDRESS_FAMILY_UNSPEC;
                }

                ThrowIfFailure(MsQuicApi.Api.ApiTable->ListenerStart(
                                   _state.Handle.QuicHandle,
                                   msquicBuffers.Buffers,
                                   (uint)applicationProtocols.Count,
                                   &address), "ListenerStart failed");
            }
            catch
            {
                _stateHandle.Free();
                throw;
            }

            Debug.Assert(!Monitor.IsEntered(_state), "!Monitor.IsEntered(_state)");
            // override the address family to the original value in case we had to use UNSPEC
            return(MsQuicParameterHelpers.GetIPEndPointParam(MsQuicApi.Api, _state.Handle, QUIC_PARAM_LISTENER_LOCAL_ADDRESS, listenEndPoint.AddressFamily));
        }
Beispiel #10
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));
        }
Beispiel #11
0
 internal override int GetRemoteAvailableBidirectionalStreamCount()
 {
     Debug.Assert(!Monitor.IsEntered(_state));
     return(MsQuicParameterHelpers.GetUShortParam(MsQuicApi.Api, _state.Handle, QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.LOCAL_BIDI_STREAM_COUNT));
 }
Beispiel #12
0
        private void SetListenPort()
        {
            SOCKADDR_INET inetAddress = MsQuicParameterHelpers.GetINetParam(MsQuicApi.Api, _ptr, (uint)QUIC_PARAM_LEVEL.LISTENER, (uint)QUIC_PARAM_LISTENER.LOCAL_ADDRESS);

            _listenEndPoint = MsQuicAddressHelpers.INetToIPEndPoint(ref inetAddress);
        }
Beispiel #13
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));
        }
Beispiel #14
0
        internal unsafe 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.");
            }

            ushort af = _remoteEndPoint.AddressFamily switch
            {
                AddressFamily.Unspecified => (ushort)QUIC_ADDRESS_FAMILY_UNSPEC,
                AddressFamily.InterNetwork => (ushort)QUIC_ADDRESS_FAMILY_INET,
                AddressFamily.InterNetworkV6 => (ushort)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;
            string targetHost;
            int    port;

            if (_remoteEndPoint is IPEndPoint ipEndPoint)
            {
                Debug.Assert(!Monitor.IsEntered(_state), "!Monitor.IsEntered(_state)");
                MsQuicParameterHelpers.SetIPEndPointParam(MsQuicApi.Api, _state.Handle, 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 (!dnsHost.Equals(_state.TargetHost, StringComparison.InvariantCultureIgnoreCase) && !string.IsNullOrEmpty(_state.TargetHost))
                {
                    targetHost = _state.TargetHost !;
                    if (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, QUIC_PARAM_CONN_REMOTE_ADDRESS, new IPEndPoint(address, port));
                    }
                    else
                    {
                        IPAddress[] addresses = Dns.GetHostAddressesAsync(dnsHost, cancellationToken).GetAwaiter().GetResult();
                        cancellationToken.ThrowIfCancellationRequested();
                        if (addresses.Length == 0)
                        {
                            throw new SocketException((int)SocketError.HostNotFound);
                        }
                        Debug.Assert(!Monitor.IsEntered(_state), "!Monitor.IsEntered(_state)");
                        // We can do something better than just using first IP but that is what
                        // MsQuic does today anyway.
                        MsQuicParameterHelpers.SetIPEndPointParam(MsQuicApi.Api, _state.Handle, QUIC_PARAM_CONN_REMOTE_ADDRESS, new IPEndPoint(addresses[0], port));
                    }
                }
                else
                {
                    // We defer everything to MsQuic.
                    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 <int>(TaskCreationOptions.RunContinuationsAsynchronously);

            IntPtr pTargetHost = Marshal.StringToCoTaskMemAnsi(targetHost);

            try
            {
                Debug.Assert(!Monitor.IsEntered(_state), "!Monitor.IsEntered(_state)");
                ThrowIfFailure(MsQuicApi.Api.ApiTable->ConnectionStart(
                                   _state.Handle.QuicHandle,
                                   _configuration.QuicHandle,
                                   af,
                                   (sbyte *)pTargetHost,
                                   (ushort)port), "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;
            }
            finally
            {
                Marshal.FreeCoTaskMem(pTargetHost);
            }

            return(new ValueTask(tcs.Task));
        }
 internal override long GetRemoteAvailableUnidirectionalStreamCount()
 {
     return(MsQuicParameterHelpers.GetUShortParam(MsQuicApi.Api, _state.Handle, QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.LOCAL_UNIDI_STREAM_COUNT));
 }
 internal override long GetRemoteAvailableBidirectionalStreamCount()
 {
     return(MsQuicParameterHelpers.GetUShortParam(MsQuicApi.Api, _ptr, (uint)QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.PEER_BIDI_STREAM_COUNT));
 }
 private unsafe void SetIdleTimeout(TimeSpan timeout)
 {
     MsQuicParameterHelpers.SetULongParam(MsQuicApi.Api, _ptr, (uint)QUIC_PARAM_LEVEL.CONNECTION, (uint)QUIC_PARAM_CONN.IDLE_TIMEOUT, (ulong)timeout.TotalMilliseconds);
 }
Beispiel #18
0
 internal int GetRemoteAvailableBidirectionalStreamCount()
 {
     Debug.Assert(!Monitor.IsEntered(_state), "!Monitor.IsEntered(_state)");
     return(MsQuicParameterHelpers.GetUShortParam(MsQuicApi.Api, _state.Handle, QUIC_PARAM_CONN_LOCAL_BIDI_STREAM_COUNT));
 }