Example #1
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;
            }
        }
Example #2
0
        // constructor for inbound connections
        public MsQuicConnection(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, SafeMsQuicConnectionHandle handle, bool remoteCertificateRequired = false, X509RevocationMode revocationMode = X509RevocationMode.Offline, RemoteCertificateValidationCallback?remoteCertificateValidationCallback = null, ServerCertificateSelectionCallback?serverCertificateSelectionCallback = null)
        {
            _state.Handle                              = handle;
            _state.StateGCHandle                       = GCHandle.Alloc(_state);
            _state.Connected                           = true;
            _state.RemoteCertificateRequired           = remoteCertificateRequired;
            _state.RevocationMode                      = revocationMode;
            _state.RemoteCertificateValidationCallback = remoteCertificateValidationCallback;
            _state.IsServer                            = true;
            _localEndPoint                             = localEndPoint;
            _remoteEndPoint                            = remoteEndPoint;

            try
            {
                Debug.Assert(!Monitor.IsEntered(_state));
                MsQuicApi.Api.SetCallbackHandlerDelegate(
                    _state.Handle,
                    s_connectionDelegate,
                    GCHandle.ToIntPtr(_state.StateGCHandle));
            }
            catch
            {
                _state.StateGCHandle.Free();
                throw;
            }

            _state.TraceId = MsQuicTraceHelper.GetTraceId(_state.Handle);
            if (NetEventSource.Log.IsEnabled())
            {
                NetEventSource.Info(_state, $"{TraceId()} Inbound connection created");
            }
        }
Example #3
0
        // constructor for inbound connections
        public MsQuicConnection(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, SafeMsQuicConnectionHandle handle)
        {
            _state.Handle              = handle;
            _state.StateGCHandle       = GCHandle.Alloc(_state);
            _state.Connected           = true;
            _localEndPoint             = localEndPoint;
            _remoteEndPoint            = remoteEndPoint;
            _remoteCertificateRequired = false;
            _isServer = true;

            try
            {
                MsQuicApi.Api.SetCallbackHandlerDelegate(
                    _state.Handle,
                    s_connectionDelegate,
                    GCHandle.ToIntPtr(_state.StateGCHandle));
            }
            catch
            {
                _state.StateGCHandle.Free();
                throw;
            }

            if (NetEventSource.Log.IsEnabled())
            {
                NetEventSource.Info(_state, $"[Connection#{_state.GetHashCode()}] inbound connection created");
            }
        }
Example #4
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);
            }
Example #5
0
        // constructor for inbound connections
        public MsQuicConnection(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, SafeMsQuicConnectionHandle handle, bool remoteCertificateRequired = false, X509RevocationMode revocationMode = X509RevocationMode.Offline, RemoteCertificateValidationCallback?remoteCertificateValidationCallback = null)
        {
            _state.Handle                        = handle;
            _state.StateGCHandle                 = GCHandle.Alloc(_state);
            _state.Connected                     = true;
            _isServer                            = true;
            _localEndPoint                       = localEndPoint;
            _remoteEndPoint                      = remoteEndPoint;
            _remoteCertificateRequired           = remoteCertificateRequired;
            _revocationMode                      = revocationMode;
            _remoteCertificateValidationCallback = remoteCertificateValidationCallback;

            if (_remoteCertificateRequired)
            {
                // We need to link connection for the validation callback.
                // We need to be able to find the connection in HandleEventPeerCertificateReceived
                // and dispatch it as sender to validation callback.
                // After that Connection will be set back to null.
                _state.Connection = this;
            }

            try
            {
                MsQuicApi.Api.SetCallbackHandlerDelegate(
                    _state.Handle,
                    s_connectionDelegate,
                    GCHandle.ToIntPtr(_state.StateGCHandle));
            }
            catch
            {
                _state.StateGCHandle.Free();
                throw;
            }

            _state.TraceId = MsQuicTraceHelper.GetTraceId(_state.Handle);
            if (NetEventSource.Log.IsEnabled())
            {
                NetEventSource.Info(_state, $"{TraceId()} Inbound connection created");
            }
        }
Example #6
0
        // constructor for inbound connections
        public MsQuicConnection(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, SafeMsQuicConnectionHandle handle)
        {
            _state.Handle    = handle;
            _state.Connected = true;
            _localEndPoint   = localEndPoint;
            _remoteEndPoint  = remoteEndPoint;

            _stateHandle = GCHandle.Alloc(_state);

            try
            {
                MsQuicApi.Api.SetCallbackHandlerDelegate(
                    _state.Handle,
                    s_connectionDelegate,
                    GCHandle.ToIntPtr(_stateHandle));
            }
            catch
            {
                _stateHandle.Free();
                throw;
            }
        }
Example #7
0
        private static unsafe uint NativeCallbackHandler(
            IntPtr listener,
            IntPtr context,
            ListenerEvent *evt)
        {
            GCHandle gcHandle = GCHandle.FromIntPtr(context);

            Debug.Assert(gcHandle.IsAllocated);
            Debug.Assert(gcHandle.Target is not null);
            var state = (State)gcHandle.Target;

            if (evt->Type != QUIC_LISTENER_EVENT.NEW_CONNECTION)
            {
                return(MsQuicStatusCodes.InternalError);
            }

            SafeMsQuicConnectionHandle?connectionHandle = null;
            MsQuicConnection?          msQuicConnection = 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);
                string     targetHost     = string.Empty; // compat with SslStream
                if (connectionInfo.ServerNameLength > 0 && connectionInfo.ServerName != IntPtr.Zero)
                {
                    // TBD We should figure out what to do with international names.
                    targetHost = Marshal.PtrToStringAnsi(connectionInfo.ServerName, connectionInfo.ServerNameLength);
                }

                SafeMsQuicConfigurationHandle?connectionConfiguration = state.ConnectionConfiguration;

                if (connectionConfiguration == null)
                {
                    Debug.Assert(state.AuthenticationOptions.ServerCertificateSelectionCallback != null);
                    try
                    {
                        // ServerCertificateSelectionCallback is synchronous. We will call it as needed when building configuration
                        connectionConfiguration = SafeMsQuicConfigurationHandle.Create(state.ConnectionOptions, state.AuthenticationOptions, targetHost);
                    }
                    catch (Exception ex)
                    {
                        if (NetEventSource.Log.IsEnabled())
                        {
                            NetEventSource.Error(state, $"[Listener#{state.GetHashCode()}] Exception occurred during creating configuration in connection callback: {ex}");
                        }
                    }

                    if (connectionConfiguration == null)
                    {
                        // We don't have safe handle yet so MsQuic will cleanup new connection.
                        return(MsQuicStatusCodes.InternalError);
                    }
                }

                connectionHandle = new SafeMsQuicConnectionHandle(evt->Data.NewConnection.Connection);

                uint status = MsQuicApi.Api.ConnectionSetConfigurationDelegate(connectionHandle, connectionConfiguration);
                if (MsQuicStatusHelper.SuccessfulStatusCode(status))
                {
                    msQuicConnection = new MsQuicConnection(localEndPoint, remoteEndPoint, state, connectionHandle, state.AuthenticationOptions.ClientCertificateRequired, state.AuthenticationOptions.CertificateRevocationCheckMode, state.AuthenticationOptions.RemoteCertificateValidationCallback);
                    msQuicConnection.SetNegotiatedAlpn(connectionInfo.NegotiatedAlpn, connectionInfo.NegotiatedAlpnLength);

                    if (!state.PendingConnections.TryAdd(connectionHandle.DangerousGetHandle(), msQuicConnection))
                    {
                        msQuicConnection.Dispose();
                    }

                    return(MsQuicStatusCodes.Success);
                }

                // If we fall-through here something wrong happened.
            }
Example #8
0
        // constructor for inbound connections
        public unsafe MsQuicConnection(IPEndPoint localEndPoint, IPEndPoint remoteEndPoint, MsQuicListener.State listenerState, SafeMsQuicConnectionHandle handle, bool remoteCertificateRequired = false, X509RevocationMode revocationMode = X509RevocationMode.Offline, RemoteCertificateValidationCallback?remoteCertificateValidationCallback = null, ServerCertificateSelectionCallback?serverCertificateSelectionCallback = null)
        {
            _state.Handle                              = handle;
            _state.StateGCHandle                       = GCHandle.Alloc(_state);
            _state.RemoteCertificateRequired           = remoteCertificateRequired;
            _state.RevocationMode                      = revocationMode;
            _state.RemoteCertificateValidationCallback = remoteCertificateValidationCallback;
            _state.IsServer                            = true;
            _localEndPoint                             = localEndPoint;
            _remoteEndPoint                            = remoteEndPoint;

            try
            {
                Debug.Assert(!Monitor.IsEntered(_state), "!Monitor.IsEntered(_state)");
                MsQuicApi.Api.ApiTable->SetConnectionCallback(_state.Handle.QuicHandle, &NativeCallback, (void *)GCHandle.ToIntPtr(_state.StateGCHandle));
            }
            catch
            {
                _state.StateGCHandle.Free();
                throw;
            }

            _state.ListenerState = listenerState;
            if (NetEventSource.Log.IsEnabled())
            {
                NetEventSource.Info(_state, $"{handle} Inbound connection created");
            }
        }
Example #9
0
#pragma warning restore CS3016
        private static unsafe int NativeCallback(QUIC_HANDLE *listener, void *context, QUIC_LISTENER_EVENT *listenerEvent)
        {
            GCHandle gcHandle = GCHandle.FromIntPtr((IntPtr)context);

            Debug.Assert(gcHandle.IsAllocated);
            Debug.Assert(gcHandle.Target is not null);
            var state = (State)gcHandle.Target;


            if (listenerEvent->Type == QUIC_LISTENER_EVENT_TYPE.STOP_COMPLETE)
            {
                state.StopCompletion.TrySetResult();
                return(QUIC_STATUS_SUCCESS);
            }

            if (listenerEvent->Type != QUIC_LISTENER_EVENT_TYPE.NEW_CONNECTION)
            {
                return(QUIC_STATUS_INTERNAL_ERROR);
            }

            SafeMsQuicConnectionHandle?connectionHandle = null;
            MsQuicConnection?          msQuicConnection = null;

            try
            {
                ref QUIC_NEW_CONNECTION_INFO connectionInfo = ref *listenerEvent->NEW_CONNECTION.Info;

                IPEndPoint localEndPoint  = MsQuicAddressHelpers.INetToIPEndPoint((IntPtr)connectionInfo.LocalAddress);
                IPEndPoint remoteEndPoint = MsQuicAddressHelpers.INetToIPEndPoint((IntPtr)connectionInfo.RemoteAddress);

                string targetHost = string.Empty;   // compat with SslStream
                if (connectionInfo.ServerNameLength > 0 && (IntPtr)connectionInfo.ServerName != IntPtr.Zero)
                {
                    // TBD We should figure out what to do with international names.
                    targetHost = Marshal.PtrToStringAnsi((IntPtr)connectionInfo.ServerName, connectionInfo.ServerNameLength);
                }

                SafeMsQuicConfigurationHandle?connectionConfiguration = state.ConnectionConfiguration;

                if (connectionConfiguration == null)
                {
                    Debug.Assert(state.AuthenticationOptions.ServerCertificateSelectionCallback != null);
                    try
                    {
                        // ServerCertificateSelectionCallback is synchronous. We will call it as needed when building configuration
                        connectionConfiguration = SafeMsQuicConfigurationHandle.Create(state.ConnectionOptions, state.AuthenticationOptions, targetHost);
                    }
                    catch (Exception ex)
                    {
                        if (NetEventSource.Log.IsEnabled())
                        {
                            NetEventSource.Error(state, $"[Listener#{state.GetHashCode()}] Exception occurred during creating configuration in connection callback: {ex}");
                        }
                    }

                    if (connectionConfiguration == null)
                    {
                        // We don't have safe handle yet so MsQuic will cleanup new connection.
                        return(QUIC_STATUS_INTERNAL_ERROR);
                    }
                }

                connectionHandle = new SafeMsQuicConnectionHandle(listenerEvent->NEW_CONNECTION.Connection);

                Debug.Assert(!Monitor.IsEntered(state), "!Monitor.IsEntered(state)");
                int status = MsQuicApi.Api.ApiTable->ConnectionSetConfiguration(connectionHandle.QuicHandle, connectionConfiguration.QuicHandle);
                if (StatusSucceeded(status))
                {
                    msQuicConnection = new MsQuicConnection(localEndPoint, remoteEndPoint, state, connectionHandle, state.AuthenticationOptions.ClientCertificateRequired, state.AuthenticationOptions.CertificateRevocationCheckMode, state.AuthenticationOptions.RemoteCertificateValidationCallback);
                    msQuicConnection.SetNegotiatedAlpn((IntPtr)connectionInfo.NegotiatedAlpn, connectionInfo.NegotiatedAlpnLength);

                    if (!state.PendingConnections.TryAdd(connectionHandle.DangerousGetHandle(), msQuicConnection))
                    {
                        msQuicConnection.Dispose();
                    }

                    return(QUIC_STATUS_SUCCESS);
                }

                // If we fall-through here something wrong happened.
            }