/// <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(); }
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); }
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"); } }
/// <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(); }
#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); } }
public unsafe SafeMsQuicStreamHandle(QUIC_HANDLE *handle) : base(handle, ptr => MsQuicApi.Api.ApiTable->StreamClose((QUIC_HANDLE *)ptr), SafeHandleType.Stream) { }
public unsafe SafeMsQuicConnectionHandle(QUIC_HANDLE *handle) : base(handle, ptr => MsQuicApi.Api.ApiTable->ConnectionClose((QUIC_HANDLE *)ptr), SafeHandleType.Connection) { }
public unsafe SafeMsQuicRegistrationHandle(QUIC_HANDLE *handle) : base(handle, ptr => MsQuicApi.Api.ApiTable->RegistrationClose((QUIC_HANDLE *)ptr), SafeHandleType.Registration) { }
#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. }
public unsafe MsQuicContextSafeHandle(QUIC_HANDLE *handle, GCHandle context, delegate *unmanaged[Cdecl] < QUIC_HANDLE *, void > releaseAction, SafeHandleType safeHandleType, MsQuicSafeHandle?parent = null)
public unsafe SafeMsQuicListenerHandle(QUIC_HANDLE *handle) : base(handle, ptr => MsQuicApi.Api.ApiTable->ListenerClose((QUIC_HANDLE *)ptr), SafeHandleType.Listener) { }
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, ®istration)); 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); } }