Example #1
0
    /// <summary>
    /// Initializes a new instance of an inbound <see cref="QuicStream" />.
    /// </summary>
    /// <param name="connectionHandle"><see cref="QuicConnection"/> safe handle, used to increment/decrement reference count with each associated stream.</param>
    /// <param name="handle">Native handle.</param>
    /// <param name="flags">Related data from the PEER_STREAM_STARTED connection event.</param>
    /// <param name="defaultErrorCode">Error code used when the stream needs to abort read or write side of the stream internally.</param>
    internal unsafe QuicStream(MsQuicContextSafeHandle connectionHandle, QUIC_HANDLE *handle, QUIC_STREAM_OPEN_FLAGS flags, long defaultErrorCode)
    {
        GCHandle context = GCHandle.Alloc(this, GCHandleType.Weak);

        try
        {
            delegate * unmanaged[Cdecl] < QUIC_HANDLE *, void *, QUIC_STREAM_EVENT *, int > nativeCallback = &NativeCallback;
            MsQuicApi.Api.ApiTable->SetCallbackHandler(
                handle,
                nativeCallback,
                (void *)GCHandle.ToIntPtr(context));
            _handle = new MsQuicContextSafeHandle(handle, context, MsQuicApi.Api.ApiTable->StreamClose, SafeHandleType.Stream, connectionHandle);
        }
        catch
        {
            context.Free();
            throw;
        }

        _defaultErrorCode = defaultErrorCode;

        _canRead  = true;
        _canWrite = !flags.HasFlag(QUIC_STREAM_OPEN_FLAGS.UNIDIRECTIONAL);
        if (!_canWrite)
        {
            _sendTcs.TrySetResult(final: true);
        }
        _id   = (long)GetMsQuicParameter <ulong>(_handle, QUIC_PARAM_STREAM_ID);
        _type = flags.HasFlag(QUIC_STREAM_OPEN_FLAGS.UNIDIRECTIONAL) ? QuicStreamType.Unidirectional : QuicStreamType.Bidirectional;
        _startedTcs.TrySetResult();
    }
Example #2
0
 private static unsafe int NativeCallback(QUIC_HANDLE *handle, void *context, QUIC_CONNECTION_EVENT *evnt)
 {
     Console.WriteLine(evnt->Type);
     if (evnt->Type == QUIC_CONNECTION_EVENT_TYPE.CONNECTED)
     {
         QUIC_API_TABLE *ApiTable = (QUIC_API_TABLE *)context;
         void *          buf      = stackalloc byte[128];
         uint            len      = 128;
         if (MsQuic.StatusSucceeded(ApiTable->GetParam(handle, MsQuic.QUIC_PARAM_CONN_REMOTE_ADDRESS, &len, buf)))
         {
             QuicAddr *addr = (QuicAddr *)(buf);
             Console.WriteLine($"Connected Family: {addr->Family}");
         }
     }
     if (evnt->Type == QUIC_CONNECTION_EVENT_TYPE.PEER_STREAM_STARTED)
     {
         Console.WriteLine("Aborting Stream");
         return(MsQuic.QUIC_STATUS_ABORTED);
     }
     if (evnt->Type == QUIC_CONNECTION_EVENT_TYPE.SHUTDOWN_INITIATED_BY_TRANSPORT)
     {
         Console.WriteLine($"{evnt->SHUTDOWN_INITIATED_BY_TRANSPORT.Status.ToString("X8")}: {MsQuicException.GetErrorCodeForStatus(evnt->SHUTDOWN_INITIATED_BY_TRANSPORT.Status)}");
     }
     return(MsQuic.QUIC_STATUS_SUCCESS);
 }
Example #3
0
        protected unsafe MsQuicSafeHandle(QUIC_HANDLE *handle, Action <IntPtr> releaseAction, SafeHandleType safeHandleType)
            : base((IntPtr)handle, ownsHandle: true)
        {
            _releaseAction = releaseAction;
            _traceId       = $"[{TypeName[(int)safeHandleType]}][0x{DangerousGetHandle():X11}]";

            if (NetEventSource.Log.IsEnabled())
            {
                NetEventSource.Info(this, "MsQuicSafeHandle created");
            }
        }
Example #4
0
    /// <summary>
    /// Initializes a new instance of an inbound <see cref="QuicConnection" />.
    /// </summary>
    /// <param name="handle">Native handle.</param>
    /// <param name="info">Related data from the NEW_CONNECTION listener event.</param>
    internal unsafe QuicConnection(QUIC_HANDLE *handle, QUIC_NEW_CONNECTION_INFO *info)
    {
        GCHandle context = GCHandle.Alloc(this, GCHandleType.Weak);

        try
        {
            delegate * unmanaged[Cdecl] < QUIC_HANDLE *, void *, QUIC_CONNECTION_EVENT *, int > nativeCallback = &NativeCallback;
            MsQuicApi.Api.ApiTable->SetCallbackHandler(
                handle,
                nativeCallback,
                (void *)GCHandle.ToIntPtr(context));
            _handle = new MsQuicContextSafeHandle(handle, context, MsQuicApi.Api.ApiTable->ConnectionClose, SafeHandleType.Connection);
        }
        catch
        {
            context.Free();
            throw;
        }

        _remoteEndPoint = info->RemoteAddress->ToIPEndPoint();
        _localEndPoint  = info->LocalAddress->ToIPEndPoint();
    }
Example #5
0
#pragma warning restore CS3016
        private static unsafe int NativeCallback(QUIC_HANDLE *connection, void *context, QUIC_CONNECTION_EVENT *connectionEvent)
        {
            GCHandle gcHandle = GCHandle.FromIntPtr((IntPtr)context);

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

            if (NetEventSource.Log.IsEnabled())
            {
                NetEventSource.Info(state, $"{state.Handle} Connection received event {connectionEvent->Type}");
            }

            try
            {
                switch (connectionEvent->Type)
                {
                case QUIC_CONNECTION_EVENT_TYPE.CONNECTED:
                    return(HandleEventConnected(state, ref *connectionEvent));

                case QUIC_CONNECTION_EVENT_TYPE.SHUTDOWN_INITIATED_BY_TRANSPORT:
                    return(HandleEventShutdownInitiatedByTransport(state, ref *connectionEvent));

                case QUIC_CONNECTION_EVENT_TYPE.SHUTDOWN_INITIATED_BY_PEER:
                    return(HandleEventShutdownInitiatedByPeer(state, ref *connectionEvent));

                case QUIC_CONNECTION_EVENT_TYPE.SHUTDOWN_COMPLETE:
                    return(HandleEventShutdownComplete(state, ref *connectionEvent));

                case QUIC_CONNECTION_EVENT_TYPE.PEER_STREAM_STARTED:
                    return(HandleEventNewStream(state, ref *connectionEvent));

                case QUIC_CONNECTION_EVENT_TYPE.STREAMS_AVAILABLE:
                    return(HandleEventStreamsAvailable(state, ref *connectionEvent));

                case QUIC_CONNECTION_EVENT_TYPE.PEER_CERTIFICATE_RECEIVED:
                    return(HandleEventPeerCertificateReceived(state, ref *connectionEvent));

                default:
                    return(QUIC_STATUS_SUCCESS);
                }
            }
            catch (Exception ex)
            {
                if (NetEventSource.Log.IsEnabled())
                {
                    NetEventSource.Error(state, $"{state.Handle} Exception occurred during handling {connectionEvent->Type} connection callback: {ex}");
                }

                if (state.ConnectTcs != null)
                {
                    // This is opportunistic if we get exception and have ability to propagate it to caller.
                    state.ConnectTcs.TrySetException(ex);
                    state.Connection = null;
                    state.ConnectTcs = null;
                }
                else
                {
                    Debug.Fail($"{state.Handle} Exception occurred during handling {connectionEvent->Type} connection callback: {ex}");
                }

                // TODO: trigger an exception on any outstanding async calls.
                return(QUIC_STATUS_INTERNAL_ERROR);
            }
        }
Example #6
0
 public unsafe SafeMsQuicStreamHandle(QUIC_HANDLE *handle)
     : base(handle, ptr => MsQuicApi.Api.ApiTable->StreamClose((QUIC_HANDLE *)ptr), SafeHandleType.Stream)
 {
 }
Example #7
0
 public unsafe SafeMsQuicConnectionHandle(QUIC_HANDLE *handle)
     : base(handle, ptr => MsQuicApi.Api.ApiTable->ConnectionClose((QUIC_HANDLE *)ptr), SafeHandleType.Connection)
 {
 }
Example #8
0
 public unsafe SafeMsQuicRegistrationHandle(QUIC_HANDLE *handle)
     : base(handle, ptr => MsQuicApi.Api.ApiTable->RegistrationClose((QUIC_HANDLE *)ptr), SafeHandleType.Registration)
 {
 }
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.
            }
Example #10
0
 public unsafe MsQuicContextSafeHandle(QUIC_HANDLE *handle, GCHandle context, delegate *unmanaged[Cdecl] < QUIC_HANDLE *, void > releaseAction, SafeHandleType safeHandleType, MsQuicSafeHandle?parent = null)
Example #11
0
 public unsafe SafeMsQuicListenerHandle(QUIC_HANDLE *handle)
     : base(handle, ptr => MsQuicApi.Api.ApiTable->ListenerClose((QUIC_HANDLE *)ptr), SafeHandleType.Listener)
 {
 }
Example #12
0
        static unsafe void Main(string[] args)
        {
            // This code lets us pass in an argument of where to search for the library at.
            // Very helpful for testing
            if (args.Length > 0)
            {
                NativeLibrary.SetDllImportResolver(typeof(MsQuic).Assembly, (libraryName, assembly, searchPath) =>
                {
                    if (libraryName != "msquic")
                    {
                        return(IntPtr.Zero);
                    }
                    if (NativeLibrary.TryLoad(args[0], out var ptr))
                    {
                        return(ptr);
                    }
                    return(IntPtr.Zero);
                });
            }

            var          ApiTable      = MsQuic.Open();
            QUIC_HANDLE *registration  = null;
            QUIC_HANDLE *configuration = null;
            QUIC_HANDLE *connection    = null;

            try
            {
                MsQuic.ThrowIfFailure(ApiTable->RegistrationOpen(null, &registration));
                byte *      alpn   = stackalloc byte[] { (byte)'h', (byte)'3' };
                QUIC_BUFFER buffer = new();
                buffer.Buffer = alpn;
                buffer.Length = 2;
                QUIC_SETTINGS settings = new();
                settings.IsSetFlags = 0;
                settings.IsSet.PeerBidiStreamCount  = 1;
                settings.PeerBidiStreamCount        = 1;
                settings.IsSet.PeerUnidiStreamCount = 1;
                settings.PeerUnidiStreamCount       = 3;
                MsQuic.ThrowIfFailure(ApiTable->ConfigurationOpen(registration, &buffer, 1, &settings, (uint)sizeof(QUIC_SETTINGS), null, &configuration));
                QUIC_CREDENTIAL_CONFIG config = new();
                config.Flags = QUIC_CREDENTIAL_FLAGS.CLIENT;
                MsQuic.ThrowIfFailure(ApiTable->ConfigurationLoadCredential(configuration, &config));
                MsQuic.ThrowIfFailure(ApiTable->ConnectionOpen(registration, &NativeCallback, ApiTable, &connection));
                sbyte *google  = stackalloc sbyte[50];
                int    written = Encoding.UTF8.GetBytes("google.com", new Span <byte>(google, 50));
                google[written] = 0;
                MsQuic.ThrowIfFailure(ApiTable->ConnectionStart(connection, configuration, 0, google, 443));
                Thread.Sleep(1000);
            }
            finally
            {
                if (connection != null)
                {
                    ApiTable->ConnectionShutdown(connection, QUIC_CONNECTION_SHUTDOWN_FLAGS.NONE, 0);
                    ApiTable->ConnectionClose(connection);
                }
                if (configuration != null)
                {
                    ApiTable->ConfigurationClose(configuration);
                }
                if (registration != null)
                {
                    ApiTable->RegistrationClose(registration);
                }
                MsQuic.Close(ApiTable);
            }
        }