diff --git a/.gitignore b/.gitignore index 2399643..2acd164 100644 --- a/.gitignore +++ b/.gitignore @@ -363,4 +363,5 @@ MigrationBackup/ FodyWeavers.xsd cmake-build-*/ -.idea/ \ No newline at end of file +.idea/ +CMakeSettings.json \ No newline at end of file diff --git a/AicsKnowledgeBase/CMakeLists.txt b/AicsKnowledgeBase/CMakeLists.txt index b49d4e5..c7aaeb8 100644 --- a/AicsKnowledgeBase/CMakeLists.txt +++ b/AicsKnowledgeBase/CMakeLists.txt @@ -9,46 +9,57 @@ find_package(Qt6 COMPONENTS Quick REQUIRED) #遍历所有Cpp文件 file(GLOB_RECURSE CPP_FILES src/*.cpp src/*.h) -foreach(filepath ${CPP_FILES}) +foreach (filepath ${CPP_FILES}) string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/" "" filename ${filepath}) list(APPEND sources_files ${filename}) -endforeach(filepath) +endforeach (filepath) #遍历所有qml文件 file(GLOB_RECURSE QML_PATHS qml/*.qml) -foreach(filepath ${QML_PATHS}) +foreach (filepath ${QML_PATHS}) string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/" "" filename ${filepath}) list(APPEND qml_files ${filename}) -endforeach(filepath) +endforeach (filepath) #遍历所有资源文件 file(GLOB_RECURSE RES_PATHS res/*.png res/*.jpg res/*.svg res/*.ico res/*.ttf res/*.webp res/qmldir) -foreach(filepath ${RES_PATHS}) +foreach (filepath ${RES_PATHS}) string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/" "" filename ${filepath}) list(APPEND resource_files ${filename}) -endforeach(filepath) +endforeach (filepath) qt_add_executable(AicsKnowledgeBase - ${sources_files} -) + ${sources_files} + ) qt_add_qml_module(AicsKnowledgeBase - URI AicsKnowledgeBase - VERSION 1.0 - QML_FILES ${qml_files} - RESOURCES ${resource_files} -) + URI AicsKnowledgeBase + VERSION 1.0 + QML_FILES ${qml_files} + RESOURCES ${resource_files} + ) set_target_properties(AicsKnowledgeBase PROPERTIES - WIN32_EXECUTABLE TRUE -) + WIN32_EXECUTABLE TRUE + ) target_compile_definitions(AicsKnowledgeBase - PRIVATE $<$,$>:QT_QML_DEBUG>) + PRIVATE $<$,$>:QT_QML_DEBUG>) + +target_include_directories(AicsKnowledgeBase PRIVATE + ../msquic/include) target_link_libraries(AicsKnowledgeBase PRIVATE - Qt6::Quick - fluentuiplugin - FramelessHelper::Core - FramelessHelper::Quick -) + Qt6::Quick + fluentuiplugin + FramelessHelper::Core + FramelessHelper::Quick + msquic.lib + ) + +file(GLOB MSQUIC_BIN_FILES ../msquic/bin/*) + +add_custom_command(TARGET AicsKnowledgeBase POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${MSQUIC_BIN_FILES} ${CMAKE_CURRENT_BINARY_DIR} + ) \ No newline at end of file diff --git a/AicsKnowledgeBase/qml/LoginPage.qml b/AicsKnowledgeBase/qml/LoginPage.qml index 1fa8de8..cb9a7bd 100644 --- a/AicsKnowledgeBase/qml/LoginPage.qml +++ b/AicsKnowledgeBase/qml/LoginPage.qml @@ -4,6 +4,7 @@ import QtQuick.Controls import QtQuick.Window import QtQuick.Layouts import org.wangwenx190.FramelessHelper +import AicsKB.HttpClient Row { anchors.fill: parent @@ -27,16 +28,21 @@ Row { width: parent.width * 0.5 height: parent.height FluPivot { - id: loginItem + Layout.fillWidth: true Layout.alignment: Qt.AlignVCenter Layout.margins: 20 height: 200 - FluPivotItem { + id: loginItem title: "登录" + function login(s) + { + showSuccess(s); + } + contentItem: Column { anchors.margins: { top: 10 @@ -44,7 +50,6 @@ Row { anchors.top: parent.top spacing: 12 - FluTextBox { anchors.left: parent.left anchors.right: parent.right @@ -76,8 +81,7 @@ Row { normalColor: FluColors.Green.normal text: "登录" onClicked: { - showSuccess("click") - + HttpClient.doGetRequest(loginItem, "login"); } } } @@ -91,6 +95,4 @@ Row { } } } - - } diff --git a/AicsKnowledgeBase/src/HttpClient.cpp b/AicsKnowledgeBase/src/HttpClient.cpp new file mode 100644 index 0000000..4e2527d --- /dev/null +++ b/AicsKnowledgeBase/src/HttpClient.cpp @@ -0,0 +1,406 @@ +// +// Created by wuyiz on 2023/6/19. +// + +#include "HttpClient.h" + +// +// The (optional) registration configuration for the app. This sets a name for +// the app (used for persistent storage and for debugging). It also configures +// the execution profile, using the default "low latency" profile. +// +const QUIC_REGISTRATION_CONFIG RegConfig = { "quicsample", QUIC_EXECUTION_PROFILE_LOW_LATENCY }; + +// +// The protocol name used in the Application Layer Protocol Negotiation (ALPN). +// +const QUIC_BUFFER Alpn = { sizeof("sample") - 1, (uint8_t*)"sample" }; + +// +// The UDP port used by the server side of the protocol. +// +const uint16_t UdpPort = 4567; + +// +// The default idle timeout period (1 second) used for the protocol. +// +const uint64_t IdleTimeoutMs = 1000; + +// +// The length of buffer sent over the streams in the protocol. +// +const uint32_t SendBufferLength = 100; + +// +// The QUIC API/function table returned from MsQuicOpen2. It contains all the +// functions called by the app to interact with MsQuic. +// +const QUIC_API_TABLE* MsQuic; + +// +// The QUIC handle to the registration object. This is the top level API object +// that represents the execution context for all work done by MsQuic on behalf +// of the app. +// +HQUIC Registration; + +// +// The QUIC handle to the configuration object. This object abstracts the +// connection configuration. This includes TLS configuration and any other +// QUIC layer settings. +// +HQUIC Configuration; + +// +// Helper function to convert a hex character to its decimal value. +// +uint8_t +DecodeHexChar( + _In_ char c +) +{ + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'A' && c <= 'F') return 10 + c - 'A'; + if (c >= 'a' && c <= 'f') return 10 + c - 'a'; + return 0; +} + +// +// Helper function to convert a string of hex characters to a byte buffer. +// +uint32_t +DecodeHexBuffer( + _In_z_ const char* HexBuffer, + _In_ uint32_t OutBufferLen, + _Out_writes_to_(OutBufferLen, return) + uint8_t* OutBuffer +) +{ + uint32_t HexBufferLen = (uint32_t)strlen(HexBuffer) / 2; + if (HexBufferLen > OutBufferLen) { + return 0; + } + + for (uint32_t i = 0; i < HexBufferLen; i++) { + OutBuffer[i] = + (DecodeHexChar(HexBuffer[i * 2]) << 4) | + DecodeHexChar(HexBuffer[i * 2 + 1]); + } + + return HexBufferLen; +} + +// +// The clients's callback for stream events from MsQuic. +// +_IRQL_requires_max_(DISPATCH_LEVEL) +_Function_class_(QUIC_STREAM_CALLBACK) +QUIC_STATUS +QUIC_API +ClientStreamCallback( + _In_ HQUIC Stream, + _In_opt_ void* Context, + _Inout_ QUIC_STREAM_EVENT* Event +) +{ + UNREFERENCED_PARAMETER(Context); + switch (Event->Type) { + case QUIC_STREAM_EVENT_SEND_COMPLETE: + // + // A previous StreamSend call has completed, and the context is being + // returned back to the app. + // + free(Event->SEND_COMPLETE.ClientContext); + printf("[strm][%p] Data sent\n", Stream); + break; + case QUIC_STREAM_EVENT_RECEIVE: + // + // Data was received from the peer on the stream. + // + printf("[strm][%p] Data received\n", Stream); + break; + case QUIC_STREAM_EVENT_PEER_SEND_ABORTED: + // + // The peer gracefully shut down its send direction of the stream. + // + printf("[strm][%p] Peer aborted\n", Stream); + break; + case QUIC_STREAM_EVENT_PEER_SEND_SHUTDOWN: + // + // The peer aborted its send direction of the stream. + // + printf("[strm][%p] Peer shut down\n", Stream); + break; + case QUIC_STREAM_EVENT_SHUTDOWN_COMPLETE: + // + // Both directions of the stream have been shut down and MsQuic is done + // with the stream. It can now be safely cleaned up. + // + printf("[strm][%p] All done\n", Stream); + if (!Event->SHUTDOWN_COMPLETE.AppCloseInProgress) { + MsQuic->StreamClose(Stream); + } + break; + default: + break; + } + return QUIC_STATUS_SUCCESS; +} + +void +ClientSend( + _In_ HQUIC Connection +) +{ + QUIC_STATUS Status; + HQUIC Stream = NULL; + uint8_t* SendBufferRaw; + QUIC_BUFFER* SendBuffer; + + // + // Create/allocate a new bidirectional stream. The stream is just allocated + // and no QUIC stream identifier is assigned until it's started. + // + if (QUIC_FAILED(Status = MsQuic->StreamOpen(Connection, QUIC_STREAM_OPEN_FLAG_NONE, ClientStreamCallback, NULL, &Stream))) { + printf("StreamOpen failed, 0x%x!\n", Status); + goto Error; + } + + printf("[strm][%p] Starting...\n", Stream); + + // + // Starts the bidirectional stream. By default, the peer is not notified of + // the stream being started until data is sent on the stream. + // + if (QUIC_FAILED(Status = MsQuic->StreamStart(Stream, QUIC_STREAM_START_FLAG_NONE))) { + printf("StreamStart failed, 0x%x!\n", Status); + MsQuic->StreamClose(Stream); + goto Error; + } + + // + // Allocates and builds the buffer to send over the stream. + // + SendBufferRaw = (uint8_t*)malloc(sizeof(QUIC_BUFFER) + SendBufferLength); + if (SendBufferRaw == NULL) { + printf("SendBuffer allocation failed!\n"); + Status = QUIC_STATUS_OUT_OF_MEMORY; + goto Error; + } + SendBuffer = (QUIC_BUFFER*)SendBufferRaw; + SendBuffer->Buffer = SendBufferRaw + sizeof(QUIC_BUFFER); + SendBuffer->Length = SendBufferLength; + + printf("[strm][%p] Sending data...\n", Stream); + + // + // Sends the buffer over the stream. Note the FIN flag is passed along with + // the buffer. This indicates this is the last buffer on the stream and the + // the stream is shut down (in the send direction) immediately after. + // + if (QUIC_FAILED(Status = MsQuic->StreamSend(Stream, SendBuffer, 1, QUIC_SEND_FLAG_FIN, SendBuffer))) { + printf("StreamSend failed, 0x%x!\n", Status); + free(SendBufferRaw); + goto Error; + } + + Error: + + if (QUIC_FAILED(Status)) { + MsQuic->ConnectionShutdown(Connection, QUIC_CONNECTION_SHUTDOWN_FLAG_NONE, 0); + } +} + +// +// The clients's callback for connection events from MsQuic. +// +_IRQL_requires_max_(DISPATCH_LEVEL) +_Function_class_(QUIC_CONNECTION_CALLBACK) +QUIC_STATUS +QUIC_API +ClientConnectionCallback( + _In_ HQUIC Connection, + _In_opt_ void* Context, + _Inout_ QUIC_CONNECTION_EVENT* Event +) +{ + UNREFERENCED_PARAMETER(Context); + switch (Event->Type) { + case QUIC_CONNECTION_EVENT_CONNECTED: + // + // The handshake has completed for the connection. + // + printf("[conn][%p] Connected\n", Connection); + ClientSend(Connection); + break; + case QUIC_CONNECTION_EVENT_SHUTDOWN_INITIATED_BY_TRANSPORT: + // + // The connection has been shut down by the transport. Generally, this + // is the expected way for the connection to shut down with this + // protocol, since we let idle timeout kill the connection. + // + if (Event->SHUTDOWN_INITIATED_BY_TRANSPORT.Status == QUIC_STATUS_CONNECTION_IDLE) { + printf("[conn][%p] Successfully shut down on idle.\n", Connection); + } else { + printf("[conn][%p] Shut down by transport, 0x%x\n", Connection, Event->SHUTDOWN_INITIATED_BY_TRANSPORT.Status); + } + break; + case QUIC_CONNECTION_EVENT_SHUTDOWN_INITIATED_BY_PEER: + // + // The connection was explicitly shut down by the peer. + // + printf("[conn][%p] Shut down by peer, 0x%llu\n", Connection, (unsigned long long)Event->SHUTDOWN_INITIATED_BY_PEER.ErrorCode); + break; + case QUIC_CONNECTION_EVENT_SHUTDOWN_COMPLETE: + // + // The connection has completed the shutdown process and is ready to be + // safely cleaned up. + // + printf("[conn][%p] All done\n", Connection); + if (!Event->SHUTDOWN_COMPLETE.AppCloseInProgress) { + MsQuic->ConnectionClose(Connection); + } + break; + case QUIC_CONNECTION_EVENT_RESUMPTION_TICKET_RECEIVED: + // + // A resumption ticket (also called New Session Ticket or NST) was + // received from the server. + // + printf("[conn][%p] Resumption ticket received (%u bytes):\n", Connection, Event->RESUMPTION_TICKET_RECEIVED.ResumptionTicketLength); + for (uint32_t i = 0; i < Event->RESUMPTION_TICKET_RECEIVED.ResumptionTicketLength; i++) { + printf("%.2X", (uint8_t)Event->RESUMPTION_TICKET_RECEIVED.ResumptionTicket[i]); + } + printf("\n"); + break; + default: + break; + } + return QUIC_STATUS_SUCCESS; +} + +// +// Helper function to load a client configuration. +// +BOOLEAN +ClientLoadConfiguration( + BOOLEAN Unsecure +) +{ + QUIC_SETTINGS Settings = {0}; + // + // Configures the client's idle timeout. + // + Settings.IdleTimeoutMs = IdleTimeoutMs; + Settings.IsSet.IdleTimeoutMs = TRUE; + + // + // Configures a default client configuration, optionally disabling + // server certificate validation. + // + QUIC_CREDENTIAL_CONFIG CredConfig; + memset(&CredConfig, 0, sizeof(CredConfig)); + CredConfig.Type = QUIC_CREDENTIAL_TYPE_NONE; + CredConfig.Flags = QUIC_CREDENTIAL_FLAG_CLIENT; + if (Unsecure) { + CredConfig.Flags |= QUIC_CREDENTIAL_FLAG_NO_CERTIFICATE_VALIDATION; + } + + // + // Allocate/initialize the configuration object, with the configured ALPN + // and settings. + // + QUIC_STATUS Status = QUIC_STATUS_SUCCESS; + if (QUIC_FAILED(Status = MsQuic->ConfigurationOpen(Registration, &Alpn, 1, &Settings, sizeof(Settings), NULL, &Configuration))) { + printf("ConfigurationOpen failed, 0x%x!\n", Status); + return FALSE; + } + + // + // Loads the TLS credential part of the configuration. This is required even + // on client side, to indicate if a certificate is required or not. + // + if (QUIC_FAILED(Status = MsQuic->ConfigurationLoadCredential(Configuration, &CredConfig))) { + printf("ConfigurationLoadCredential failed, 0x%x!\n", Status); + return FALSE; + } + + return TRUE; +} + +// +// Runs the client side of the protocol. +// +void +RunClient( + _In_ int argc, + _In_reads_(argc) _Null_terminated_ char* argv[] +) +{ + // + // Get the target / server name or IP from the command line. + // + const char* Target = ""; + + // + // Load the client configuration based on the "unsecure" command line option. + // + if (!ClientLoadConfiguration(true)) { + return; + } + + QUIC_STATUS Status; + const char* ResumptionTicketString = NULL; + HQUIC Connection = NULL; + + // + // Allocate a new connection object. + // + if (QUIC_FAILED(Status = MsQuic->ConnectionOpen(Registration, ClientConnectionCallback, NULL, &Connection))) { + printf("ConnectionOpen failed, 0x%x!\n", Status); + goto Error; + } + + if ((ResumptionTicketString = NULL) != NULL) { + // + // If provided at the command line, set the resumption ticket that can + // be used to resume a previous session. + // + uint8_t ResumptionTicket[10240]; + uint16_t TicketLength = (uint16_t)DecodeHexBuffer(ResumptionTicketString, sizeof(ResumptionTicket), ResumptionTicket); + if (QUIC_FAILED(Status = MsQuic->SetParam(Connection, QUIC_PARAM_CONN_RESUMPTION_TICKET, TicketLength, ResumptionTicket))) { + printf("SetParam(QUIC_PARAM_CONN_RESUMPTION_TICKET) failed, 0x%x!\n", Status); + goto Error; + } + } + + printf("[conn][%p] Connecting...\n", Connection); + + // + // Start the connection to the server. + // + if (QUIC_FAILED(Status = MsQuic->ConnectionStart(Connection, Configuration, QUIC_ADDRESS_FAMILY_UNSPEC, Target, UdpPort))) { + printf("ConnectionStart failed, 0x%x!\n", Status); + goto Error; + } + + Error: + + if (QUIC_FAILED(Status) && Connection != NULL) { + MsQuic->ConnectionClose(Connection); + } +} + + + +HttpClient::HttpClient(QQmlApplicationEngine &engine, QObject *parent) + : engine(engine), QObject(parent) +{ +} + +void HttpClient::doGetRequest(QObject *object, QString callback) +{ + QVariant res; + QMetaObject::invokeMethod(object, callback.toStdString().c_str(), + Q_RETURN_ARG(QVariant, res), + Q_ARG(QVariant, "test")); +} diff --git a/AicsKnowledgeBase/src/HttpClient.h b/AicsKnowledgeBase/src/HttpClient.h new file mode 100644 index 0000000..7682644 --- /dev/null +++ b/AicsKnowledgeBase/src/HttpClient.h @@ -0,0 +1,24 @@ +// +// Created by wuyiz on 2023/6/19. +// + +#ifndef AICSKNOWLEDGEBASE_HTTPCLIENT_H +#define AICSKNOWLEDGEBASE_HTTPCLIENT_H + +#include +#include +#include + +class HttpClient : public QObject +{ +Q_OBJECT +public: + explicit HttpClient(QQmlApplicationEngine &engine, QObject *parent = nullptr); + Q_INVOKABLE void doGetRequest(QObject* object, QString callback); + +private: + QQmlApplicationEngine &engine; +}; + + +#endif //AICSKNOWLEDGEBASE_HTTPCLIENT_H diff --git a/AicsKnowledgeBase/src/main.cpp b/AicsKnowledgeBase/src/main.cpp index b5e6731..092fd85 100644 --- a/AicsKnowledgeBase/src/main.cpp +++ b/AicsKnowledgeBase/src/main.cpp @@ -6,6 +6,7 @@ #include #include #include +#include "HttpClient.h" FRAMELESSHELPER_USE_NAMESPACE @@ -15,6 +16,7 @@ int main(int argc, char* argv[]) qputenv("QT_QUICK_CONTROLS_STYLE", "Basic"); FramelessHelper::Quick::initialize(); QGuiApplication app(argc, argv); + #ifdef Q_OS_WIN // 此设置仅在Windows下生效 FramelessConfig::instance()->set(Global::Option::ForceHideWindowFrameBorder); #endif @@ -28,12 +30,18 @@ int main(int argc, char* argv[]) QQmlApplicationEngine engine; FramelessHelper::Quick::registerTypes(&engine); - const QUrl url(u"qrc:/AicsKnowledgeBase/qml/App.qml"_qs); + HttpClient* httpClient = new HttpClient(engine); + qmlRegisterSingletonInstance("AicsKB.HttpClient", 1, 0, "HttpClient", httpClient); + + + const QUrl url(u"qrc:/AicsKnowledgeBase/qml/App.qml"_qs); QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, &app, [url](QObject* obj, const QUrl& objUrl) { if (!obj && url == objUrl) QCoreApplication::exit(-1); }, Qt::QueuedConnection); engine.load(url); - return app.exec(); + auto result = app.exec(); + httpClient->deleteLater(); + return result; } diff --git a/CMakeLists.txt b/CMakeLists.txt index 305f14f..d1ed3cd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,4 +3,8 @@ cmake_minimum_required(VERSION 3.20) project(AicsKnowledgeBase LANGUAGES CXX) add_subdirectory(FluentUI) + +link_directories(msquic/lib) +link_directories(msquic/bin) add_subdirectory(AicsKnowledgeBase) +add_subdirectory(MsQuicSample) \ No newline at end of file diff --git a/MsQuicSample/CMakeLists.txt b/MsQuicSample/CMakeLists.txt new file mode 100644 index 0000000..dbed4bf --- /dev/null +++ b/MsQuicSample/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 3.16) + +project(MsQuicSample LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +add_executable(MsQuicSample + sample.cpp + ) + +target_include_directories(MsQuicSample PRIVATE + ../msquic/include) + +target_link_libraries(MsQuicSample PRIVATE + msquic + ) + +file(GLOB MSQUIC_BIN_FILES ../msquic/bin/*) + +add_custom_command(TARGET MsQuicSample POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${MSQUIC_BIN_FILES} ${CMAKE_CURRENT_BINARY_DIR} + ) diff --git a/MsQuicSample/sample.cpp b/MsQuicSample/sample.cpp new file mode 100644 index 0000000..e63231c --- /dev/null +++ b/MsQuicSample/sample.cpp @@ -0,0 +1,907 @@ +/*++ + + Copyright (c) Microsoft Corporation. + Licensed under the MIT License. + +Abstract: + + Provides a very simple MsQuic API sample server and client application. + + The quicsample app implements a simple protocol (ALPN "sample") where the + client connects to the server, opens a single bidirectional stream, sends + some data and shuts down the stream in the send direction. On the server + side all connections, streams and data are accepted. After the stream is + shut down, the server then sends its own data and shuts down its send + direction. The connection only shuts down when the 1 second idle timeout + triggers. + + A certificate needs to be available for the server to function. + + On Windows, the following PowerShell command can be used to generate a self + signed certificate with the correct settings. This works for both Schannel + and OpenSSL TLS providers, assuming the KeyExportPolicy parameter is set to + Exportable. The Thumbprint received from the command is then passed to this + sample with -cert_hash:PASTE_THE_THUMBPRINT_HERE + + New-SelfSignedCertificate -DnsName $env:computername,localhost -FriendlyName MsQuic-Test -KeyUsageProperty Sign -KeyUsage DigitalSignature -CertStoreLocation cert:\CurrentUser\My -HashAlgorithm SHA256 -Provider "Microsoft Software Key Storage Provider" -KeyExportPolicy Exportable + + On Linux, the following command can be used to generate a self signed + certificate that works with the OpenSSL TLS Provider. This can also be used + for Windows OpenSSL, however we recommend the certificate store method above + for ease of use. Currently key files with password protections are not + supported. With these files, they can be passed to the sample with + -cert_file:path/to/server.cert -key_file path/to/server.key + + openssl req -nodes -new -x509 -keyout server.key -out server.cert + +--*/ + +#ifdef _WIN32 +// +// The conformant preprocessor along with the newest SDK throws this warning for +// a macro in C mode. As users might run into this exact bug, exclude this +// warning here. This is not an MsQuic bug but a Windows SDK bug. +// +#pragma warning(disable:5105) +#endif +#include "msquic.h" +#include +#include + +#ifndef UNREFERENCED_PARAMETER +#define UNREFERENCED_PARAMETER(P) (void)(P) +#endif + +// +// The (optional) registration configuration for the app. This sets a name for +// the app (used for persistent storage and for debugging). It also configures +// the execution profile, using the default "low latency" profile. +// +const QUIC_REGISTRATION_CONFIG RegConfig = { "quicsample", QUIC_EXECUTION_PROFILE_LOW_LATENCY }; + +// +// The protocol name used in the Application Layer Protocol Negotiation (ALPN). +// +const QUIC_BUFFER Alpn = { sizeof("sample") - 1, (uint8_t*)"sample" }; + +// +// The UDP port used by the server side of the protocol. +// +const uint16_t UdpPort = 4567; + +// +// The default idle timeout period (1 second) used for the protocol. +// +const uint64_t IdleTimeoutMs = 1000; + +// +// The length of buffer sent over the streams in the protocol. +// +const uint32_t SendBufferLength = 100; + +// +// The QUIC API/function table returned from MsQuicOpen2. It contains all the +// functions called by the app to interact with MsQuic. +// +const QUIC_API_TABLE* MsQuic; + +// +// The QUIC handle to the registration object. This is the top level API object +// that represents the execution context for all work done by MsQuic on behalf +// of the app. +// +HQUIC Registration; + +// +// The QUIC handle to the configuration object. This object abstracts the +// connection configuration. This includes TLS configuration and any other +// QUIC layer settings. +// +HQUIC Configuration; + +void PrintUsage() +{ + printf( + "\n" + "quicsample runs a simple client or server.\n" + "\n" + "Usage:\n" + "\n" + " quicsample.exe -client -unsecure -target:{IPAddress|Hostname} [-ticket:]\n" + " quicsample.exe -server -cert_hash:<...>\n" + " quicsample.exe -server -cert_file:<...> -key_file:<...> [-password:<...>]\n" + ); +} + +// +// Helper functions to look up a command line arguments. +// +BOOLEAN +GetFlag( + _In_ int argc, + _In_reads_(argc) _Null_terminated_ char* argv[], + _In_z_ const char* name +) +{ + const size_t nameLen = strlen(name); + for (int i = 0; i < argc; i++) { + if (_strnicmp(argv[i] + 1, name, nameLen) == 0 + && strlen(argv[i]) == nameLen + 1) { + return TRUE; + } + } + return FALSE; +} + +_Ret_maybenull_ _Null_terminated_ const char* +GetValue( + _In_ int argc, + _In_reads_(argc) _Null_terminated_ char* argv[], + _In_z_ const char* name +) +{ + const size_t nameLen = strlen(name); + for (int i = 0; i < argc; i++) { + if (_strnicmp(argv[i] + 1, name, nameLen) == 0 + && strlen(argv[i]) > 1 + nameLen + 1 + && *(argv[i] + 1 + nameLen) == ':') { + return argv[i] + 1 + nameLen + 1; + } + } + return NULL; +} + +// +// Helper function to convert a hex character to its decimal value. +// +uint8_t +DecodeHexChar( + _In_ char c +) +{ + if (c >= '0' && c <= '9') return c - '0'; + if (c >= 'A' && c <= 'F') return 10 + c - 'A'; + if (c >= 'a' && c <= 'f') return 10 + c - 'a'; + return 0; +} + +// +// Helper function to convert a string of hex characters to a byte buffer. +// +uint32_t +DecodeHexBuffer( + _In_z_ const char* HexBuffer, + _In_ uint32_t OutBufferLen, + _Out_writes_to_(OutBufferLen, return) + uint8_t* OutBuffer +) +{ + uint32_t HexBufferLen = (uint32_t)strlen(HexBuffer) / 2; + if (HexBufferLen > OutBufferLen) { + return 0; + } + + for (uint32_t i = 0; i < HexBufferLen; i++) { + OutBuffer[i] = + (DecodeHexChar(HexBuffer[i * 2]) << 4) | + DecodeHexChar(HexBuffer[i * 2 + 1]); + } + + return HexBufferLen; +} + +// +// Allocates and sends some data over a QUIC stream. +// +void +ServerSend( + _In_ HQUIC Stream +) +{ + // + // Allocates and builds the buffer to send over the stream. + // + void* SendBufferRaw = malloc(sizeof(QUIC_BUFFER) + SendBufferLength); + if (SendBufferRaw == NULL) { + printf("SendBuffer allocation failed!\n"); + MsQuic->StreamShutdown(Stream, QUIC_STREAM_SHUTDOWN_FLAG_ABORT, 0); + return; + } + QUIC_BUFFER* SendBuffer = (QUIC_BUFFER*)SendBufferRaw; + SendBuffer->Buffer = (uint8_t*)SendBufferRaw + sizeof(QUIC_BUFFER); + SendBuffer->Length = SendBufferLength; + + printf("[strm][%p] Sending data...\n", Stream); + + // + // Sends the buffer over the stream. Note the FIN flag is passed along with + // the buffer. This indicates this is the last buffer on the stream and the + // the stream is shut down (in the send direction) immediately after. + // + QUIC_STATUS Status; + if (QUIC_FAILED(Status = MsQuic->StreamSend(Stream, SendBuffer, 1, QUIC_SEND_FLAG_FIN, SendBuffer))) { + printf("StreamSend failed, 0x%x!\n", Status); + free(SendBufferRaw); + MsQuic->StreamShutdown(Stream, QUIC_STREAM_SHUTDOWN_FLAG_ABORT, 0); + } +} + +// +// The server's callback for stream events from MsQuic. +// +_IRQL_requires_max_(DISPATCH_LEVEL) +_Function_class_(QUIC_STREAM_CALLBACK) +QUIC_STATUS +QUIC_API +ServerStreamCallback( + _In_ HQUIC Stream, + _In_opt_ void* Context, + _Inout_ QUIC_STREAM_EVENT* Event +) +{ + UNREFERENCED_PARAMETER(Context); + switch (Event->Type) { + case QUIC_STREAM_EVENT_SEND_COMPLETE: + // + // A previous StreamSend call has completed, and the context is being + // returned back to the app. + // + free(Event->SEND_COMPLETE.ClientContext); + printf("[strm][%p] Data sent\n", Stream); + break; + case QUIC_STREAM_EVENT_RECEIVE: + // + // Data was received from the peer on the stream. + // + printf("[strm][%p] Data received\n", Stream); + break; + case QUIC_STREAM_EVENT_PEER_SEND_SHUTDOWN: + // + // The peer gracefully shut down its send direction of the stream. + // + printf("[strm][%p] Peer shut down\n", Stream); + ServerSend(Stream); + break; + case QUIC_STREAM_EVENT_PEER_SEND_ABORTED: + // + // The peer aborted its send direction of the stream. + // + printf("[strm][%p] Peer aborted\n", Stream); + MsQuic->StreamShutdown(Stream, QUIC_STREAM_SHUTDOWN_FLAG_ABORT, 0); + break; + case QUIC_STREAM_EVENT_SHUTDOWN_COMPLETE: + // + // Both directions of the stream have been shut down and MsQuic is done + // with the stream. It can now be safely cleaned up. + // + printf("[strm][%p] All done\n", Stream); + MsQuic->StreamClose(Stream); + break; + default: + break; + } + return QUIC_STATUS_SUCCESS; +} + +// +// The server's callback for connection events from MsQuic. +// +_IRQL_requires_max_(DISPATCH_LEVEL) +_Function_class_(QUIC_CONNECTION_CALLBACK) +QUIC_STATUS +QUIC_API +ServerConnectionCallback( + _In_ HQUIC Connection, + _In_opt_ void* Context, + _Inout_ QUIC_CONNECTION_EVENT* Event +) +{ + UNREFERENCED_PARAMETER(Context); + switch (Event->Type) { + case QUIC_CONNECTION_EVENT_CONNECTED: + // + // The handshake has completed for the connection. + // + printf("[conn][%p] Connected\n", Connection); + MsQuic->ConnectionSendResumptionTicket(Connection, QUIC_SEND_RESUMPTION_FLAG_NONE, 0, NULL); + break; + case QUIC_CONNECTION_EVENT_SHUTDOWN_INITIATED_BY_TRANSPORT: + // + // The connection has been shut down by the transport. Generally, this + // is the expected way for the connection to shut down with this + // protocol, since we let idle timeout kill the connection. + // + if (Event->SHUTDOWN_INITIATED_BY_TRANSPORT.Status == QUIC_STATUS_CONNECTION_IDLE) { + printf("[conn][%p] Successfully shut down on idle.\n", Connection); + } else { + printf("[conn][%p] Shut down by transport, 0x%x\n", Connection, Event->SHUTDOWN_INITIATED_BY_TRANSPORT.Status); + } + break; + case QUIC_CONNECTION_EVENT_SHUTDOWN_INITIATED_BY_PEER: + // + // The connection was explicitly shut down by the peer. + // + printf("[conn][%p] Shut down by peer, 0x%llu\n", Connection, (unsigned long long)Event->SHUTDOWN_INITIATED_BY_PEER.ErrorCode); + break; + case QUIC_CONNECTION_EVENT_SHUTDOWN_COMPLETE: + // + // The connection has completed the shutdown process and is ready to be + // safely cleaned up. + // + printf("[conn][%p] All done\n", Connection); + MsQuic->ConnectionClose(Connection); + break; + case QUIC_CONNECTION_EVENT_PEER_STREAM_STARTED: + // + // The peer has started/created a new stream. The app MUST set the + // callback handler before returning. + // + printf("[strm][%p] Peer started\n", Event->PEER_STREAM_STARTED.Stream); + MsQuic->SetCallbackHandler(Event->PEER_STREAM_STARTED.Stream, (void*)ServerStreamCallback, NULL); + break; + case QUIC_CONNECTION_EVENT_RESUMED: + // + // The connection succeeded in doing a TLS resumption of a previous + // connection's session. + // + printf("[conn][%p] Connection resumed!\n", Connection); + break; + default: + break; + } + return QUIC_STATUS_SUCCESS; +} + +// +// The server's callback for listener events from MsQuic. +// +_IRQL_requires_max_(PASSIVE_LEVEL) +_Function_class_(QUIC_LISTENER_CALLBACK) +QUIC_STATUS +QUIC_API +ServerListenerCallback( + _In_ HQUIC Listener, + _In_opt_ void* Context, + _Inout_ QUIC_LISTENER_EVENT* Event +) +{ + UNREFERENCED_PARAMETER(Listener); + UNREFERENCED_PARAMETER(Context); + QUIC_STATUS Status = QUIC_STATUS_NOT_SUPPORTED; + switch (Event->Type) { + case QUIC_LISTENER_EVENT_NEW_CONNECTION: + // + // A new connection is being attempted by a client. For the handshake to + // proceed, the server must provide a configuration for QUIC to use. The + // app MUST set the callback handler before returning. + // + MsQuic->SetCallbackHandler(Event->NEW_CONNECTION.Connection, (void*)ServerConnectionCallback, NULL); + Status = MsQuic->ConnectionSetConfiguration(Event->NEW_CONNECTION.Connection, Configuration); + break; + default: + break; + } + return Status; +} + +typedef struct QUIC_CREDENTIAL_CONFIG_HELPER { + QUIC_CREDENTIAL_CONFIG CredConfig; + union { + QUIC_CERTIFICATE_HASH CertHash; + QUIC_CERTIFICATE_HASH_STORE CertHashStore; + QUIC_CERTIFICATE_FILE CertFile; + QUIC_CERTIFICATE_FILE_PROTECTED CertFileProtected; + }; +} QUIC_CREDENTIAL_CONFIG_HELPER; + +// +// Helper function to load a server configuration. Uses the command line +// arguments to load the credential part of the configuration. +// +BOOLEAN +ServerLoadConfiguration( + _In_ int argc, + _In_reads_(argc) _Null_terminated_ char* argv[] +) +{ + QUIC_SETTINGS Settings = {0}; + // + // Configures the server's idle timeout. + // + Settings.IdleTimeoutMs = IdleTimeoutMs; + Settings.IsSet.IdleTimeoutMs = TRUE; + // + // Configures the server's resumption level to allow for resumption and + // 0-RTT. + // + Settings.ServerResumptionLevel = QUIC_SERVER_RESUME_AND_ZERORTT; + Settings.IsSet.ServerResumptionLevel = TRUE; + // + // Configures the server's settings to allow for the peer to open a single + // bidirectional stream. By default connections are not configured to allow + // any streams from the peer. + // + Settings.PeerBidiStreamCount = 1; + Settings.IsSet.PeerBidiStreamCount = TRUE; + + QUIC_CREDENTIAL_CONFIG_HELPER Config; + memset(&Config, 0, sizeof(Config)); + Config.CredConfig.Flags = QUIC_CREDENTIAL_FLAG_NONE; + + const char* Cert; + const char* KeyFile; + if ((Cert = GetValue(argc, argv, "cert_hash")) != NULL) { + // + // Load the server's certificate from the default certificate store, + // using the provided certificate hash. + // + uint32_t CertHashLen = + DecodeHexBuffer( + Cert, + sizeof(Config.CertHash.ShaHash), + Config.CertHash.ShaHash); + if (CertHashLen != sizeof(Config.CertHash.ShaHash)) { + return FALSE; + } + Config.CredConfig.Type = QUIC_CREDENTIAL_TYPE_CERTIFICATE_HASH; + Config.CredConfig.CertificateHash = &Config.CertHash; + + } else if ((Cert = GetValue(argc, argv, "cert_file")) != NULL && + (KeyFile = GetValue(argc, argv, "key_file")) != NULL) { + // + // Loads the server's certificate from the file. + // + const char* Password = GetValue(argc, argv, "password"); + if (Password != NULL) { + Config.CertFileProtected.CertificateFile = (char*)Cert; + Config.CertFileProtected.PrivateKeyFile = (char*)KeyFile; + Config.CertFileProtected.PrivateKeyPassword = (char*)Password; + Config.CredConfig.Type = QUIC_CREDENTIAL_TYPE_CERTIFICATE_FILE_PROTECTED; + Config.CredConfig.CertificateFileProtected = &Config.CertFileProtected; + } else { + Config.CertFile.CertificateFile = (char*)Cert; + Config.CertFile.PrivateKeyFile = (char*)KeyFile; + Config.CredConfig.Type = QUIC_CREDENTIAL_TYPE_CERTIFICATE_FILE; + Config.CredConfig.CertificateFile = &Config.CertFile; + } + + } else { + printf("Must specify ['-cert_hash'] or ['cert_file' and 'key_file' (and optionally 'password')]!\n"); + return FALSE; + } + + // + // Allocate/initialize the configuration object, with the configured ALPN + // and settings. + // + QUIC_STATUS Status = QUIC_STATUS_SUCCESS; + if (QUIC_FAILED(Status = MsQuic->ConfigurationOpen(Registration, &Alpn, 1, &Settings, sizeof(Settings), NULL, &Configuration))) { + printf("ConfigurationOpen failed, 0x%x!\n", Status); + return FALSE; + } + + // + // Loads the TLS credential part of the configuration. + // + if (QUIC_FAILED(Status = MsQuic->ConfigurationLoadCredential(Configuration, &Config.CredConfig))) { + printf("ConfigurationLoadCredential failed, 0x%x!\n", Status); + return FALSE; + } + + return TRUE; +} + +// +// Runs the server side of the protocol. +// +void +RunServer( + _In_ int argc, + _In_reads_(argc) _Null_terminated_ char* argv[] +) +{ + QUIC_STATUS Status; + HQUIC Listener = NULL; + + // + // Configures the address used for the listener to listen on all IP + // addresses and the given UDP port. + // + QUIC_ADDR Address = {0}; + QuicAddrSetFamily(&Address, QUIC_ADDRESS_FAMILY_UNSPEC); + QuicAddrSetPort(&Address, UdpPort); + + // + // Load the server configuration based on the command line. + // + if (!ServerLoadConfiguration(argc, argv)) { + return; + } + + // + // Create/allocate a new listener object. + // + if (QUIC_FAILED(Status = MsQuic->ListenerOpen(Registration, ServerListenerCallback, NULL, &Listener))) { + printf("ListenerOpen failed, 0x%x!\n", Status); + goto Error; + } + + // + // Starts listening for incoming connections. + // + if (QUIC_FAILED(Status = MsQuic->ListenerStart(Listener, &Alpn, 1, &Address))) { + printf("ListenerStart failed, 0x%x!\n", Status); + goto Error; + } + + // + // Continue listening for connections until the Enter key is pressed. + // + printf("Press Enter to exit.\n\n"); + getchar(); + + Error: + + if (Listener != NULL) { + MsQuic->ListenerClose(Listener); + } +} + +// +// The clients's callback for stream events from MsQuic. +// +_IRQL_requires_max_(DISPATCH_LEVEL) +_Function_class_(QUIC_STREAM_CALLBACK) +QUIC_STATUS +QUIC_API +ClientStreamCallback( + _In_ HQUIC Stream, + _In_opt_ void* Context, + _Inout_ QUIC_STREAM_EVENT* Event +) +{ + UNREFERENCED_PARAMETER(Context); + switch (Event->Type) { + case QUIC_STREAM_EVENT_SEND_COMPLETE: + // + // A previous StreamSend call has completed, and the context is being + // returned back to the app. + // + free(Event->SEND_COMPLETE.ClientContext); + printf("[strm][%p] Data sent\n", Stream); + break; + case QUIC_STREAM_EVENT_RECEIVE: + // + // Data was received from the peer on the stream. + // + printf("[strm][%p] Data received\n", Stream); + break; + case QUIC_STREAM_EVENT_PEER_SEND_ABORTED: + // + // The peer gracefully shut down its send direction of the stream. + // + printf("[strm][%p] Peer aborted\n", Stream); + break; + case QUIC_STREAM_EVENT_PEER_SEND_SHUTDOWN: + // + // The peer aborted its send direction of the stream. + // + printf("[strm][%p] Peer shut down\n", Stream); + break; + case QUIC_STREAM_EVENT_SHUTDOWN_COMPLETE: + // + // Both directions of the stream have been shut down and MsQuic is done + // with the stream. It can now be safely cleaned up. + // + printf("[strm][%p] All done\n", Stream); + if (!Event->SHUTDOWN_COMPLETE.AppCloseInProgress) { + MsQuic->StreamClose(Stream); + } + break; + default: + break; + } + return QUIC_STATUS_SUCCESS; +} + +void +ClientSend( + _In_ HQUIC Connection +) +{ + QUIC_STATUS Status; + HQUIC Stream = NULL; + uint8_t* SendBufferRaw; + QUIC_BUFFER* SendBuffer; + + // + // Create/allocate a new bidirectional stream. The stream is just allocated + // and no QUIC stream identifier is assigned until it's started. + // + if (QUIC_FAILED(Status = MsQuic->StreamOpen(Connection, QUIC_STREAM_OPEN_FLAG_NONE, ClientStreamCallback, NULL, &Stream))) { + printf("StreamOpen failed, 0x%x!\n", Status); + goto Error; + } + + printf("[strm][%p] Starting...\n", Stream); + + // + // Starts the bidirectional stream. By default, the peer is not notified of + // the stream being started until data is sent on the stream. + // + if (QUIC_FAILED(Status = MsQuic->StreamStart(Stream, QUIC_STREAM_START_FLAG_NONE))) { + printf("StreamStart failed, 0x%x!\n", Status); + MsQuic->StreamClose(Stream); + goto Error; + } + + // + // Allocates and builds the buffer to send over the stream. + // + SendBufferRaw = (uint8_t*)malloc(sizeof(QUIC_BUFFER) + SendBufferLength); + if (SendBufferRaw == NULL) { + printf("SendBuffer allocation failed!\n"); + Status = QUIC_STATUS_OUT_OF_MEMORY; + goto Error; + } + SendBuffer = (QUIC_BUFFER*)SendBufferRaw; + SendBuffer->Buffer = SendBufferRaw + sizeof(QUIC_BUFFER); + SendBuffer->Length = SendBufferLength; + + printf("[strm][%p] Sending data...\n", Stream); + + // + // Sends the buffer over the stream. Note the FIN flag is passed along with + // the buffer. This indicates this is the last buffer on the stream and the + // the stream is shut down (in the send direction) immediately after. + // + if (QUIC_FAILED(Status = MsQuic->StreamSend(Stream, SendBuffer, 1, QUIC_SEND_FLAG_FIN, SendBuffer))) { + printf("StreamSend failed, 0x%x!\n", Status); + free(SendBufferRaw); + goto Error; + } + + Error: + + if (QUIC_FAILED(Status)) { + MsQuic->ConnectionShutdown(Connection, QUIC_CONNECTION_SHUTDOWN_FLAG_NONE, 0); + } +} + +// +// The clients's callback for connection events from MsQuic. +// +_IRQL_requires_max_(DISPATCH_LEVEL) +_Function_class_(QUIC_CONNECTION_CALLBACK) +QUIC_STATUS +QUIC_API +ClientConnectionCallback( + _In_ HQUIC Connection, + _In_opt_ void* Context, + _Inout_ QUIC_CONNECTION_EVENT* Event +) +{ + UNREFERENCED_PARAMETER(Context); + switch (Event->Type) { + case QUIC_CONNECTION_EVENT_CONNECTED: + // + // The handshake has completed for the connection. + // + printf("[conn][%p] Connected\n", Connection); + ClientSend(Connection); + break; + case QUIC_CONNECTION_EVENT_SHUTDOWN_INITIATED_BY_TRANSPORT: + // + // The connection has been shut down by the transport. Generally, this + // is the expected way for the connection to shut down with this + // protocol, since we let idle timeout kill the connection. + // + if (Event->SHUTDOWN_INITIATED_BY_TRANSPORT.Status == QUIC_STATUS_CONNECTION_IDLE) { + printf("[conn][%p] Successfully shut down on idle.\n", Connection); + } else { + printf("[conn][%p] Shut down by transport, 0x%x\n", Connection, Event->SHUTDOWN_INITIATED_BY_TRANSPORT.Status); + } + break; + case QUIC_CONNECTION_EVENT_SHUTDOWN_INITIATED_BY_PEER: + // + // The connection was explicitly shut down by the peer. + // + printf("[conn][%p] Shut down by peer, 0x%llu\n", Connection, (unsigned long long)Event->SHUTDOWN_INITIATED_BY_PEER.ErrorCode); + break; + case QUIC_CONNECTION_EVENT_SHUTDOWN_COMPLETE: + // + // The connection has completed the shutdown process and is ready to be + // safely cleaned up. + // + printf("[conn][%p] All done\n", Connection); + if (!Event->SHUTDOWN_COMPLETE.AppCloseInProgress) { + MsQuic->ConnectionClose(Connection); + } + break; + case QUIC_CONNECTION_EVENT_RESUMPTION_TICKET_RECEIVED: + // + // A resumption ticket (also called New Session Ticket or NST) was + // received from the server. + // + printf("[conn][%p] Resumption ticket received (%u bytes):\n", Connection, Event->RESUMPTION_TICKET_RECEIVED.ResumptionTicketLength); + for (uint32_t i = 0; i < Event->RESUMPTION_TICKET_RECEIVED.ResumptionTicketLength; i++) { + printf("%.2X", (uint8_t)Event->RESUMPTION_TICKET_RECEIVED.ResumptionTicket[i]); + } + printf("\n"); + break; + default: + break; + } + return QUIC_STATUS_SUCCESS; +} + +// +// Helper function to load a client configuration. +// +BOOLEAN +ClientLoadConfiguration( + BOOLEAN Unsecure +) +{ + QUIC_SETTINGS Settings = {0}; + // + // Configures the client's idle timeout. + // + Settings.IdleTimeoutMs = IdleTimeoutMs; + Settings.IsSet.IdleTimeoutMs = TRUE; + + // + // Configures a default client configuration, optionally disabling + // server certificate validation. + // + QUIC_CREDENTIAL_CONFIG CredConfig; + memset(&CredConfig, 0, sizeof(CredConfig)); + CredConfig.Type = QUIC_CREDENTIAL_TYPE_NONE; + CredConfig.Flags = QUIC_CREDENTIAL_FLAG_CLIENT; + if (Unsecure) { + CredConfig.Flags |= QUIC_CREDENTIAL_FLAG_NO_CERTIFICATE_VALIDATION; + } + + // + // Allocate/initialize the configuration object, with the configured ALPN + // and settings. + // + QUIC_STATUS Status = QUIC_STATUS_SUCCESS; + if (QUIC_FAILED(Status = MsQuic->ConfigurationOpen(Registration, &Alpn, 1, &Settings, sizeof(Settings), NULL, &Configuration))) { + printf("ConfigurationOpen failed, 0x%x!\n", Status); + return FALSE; + } + + // + // Loads the TLS credential part of the configuration. This is required even + // on client side, to indicate if a certificate is required or not. + // + if (QUIC_FAILED(Status = MsQuic->ConfigurationLoadCredential(Configuration, &CredConfig))) { + printf("ConfigurationLoadCredential failed, 0x%x!\n", Status); + return FALSE; + } + + return TRUE; +} + +// +// Runs the client side of the protocol. +// +void +RunClient( + _In_ int argc, + _In_reads_(argc) _Null_terminated_ char* argv[] +) +{ + // + // Load the client configuration based on the "unsecure" command line option. + // + if (!ClientLoadConfiguration(GetFlag(argc, argv, "unsecure"))) { + return; + } + + QUIC_STATUS Status; + const char* ResumptionTicketString = NULL; + HQUIC Connection = NULL; + + // + // Allocate a new connection object. + // + if (QUIC_FAILED(Status = MsQuic->ConnectionOpen(Registration, ClientConnectionCallback, NULL, &Connection))) { + printf("ConnectionOpen failed, 0x%x!\n", Status); + goto Error; + } + + if ((ResumptionTicketString = GetValue(argc, argv, "ticket")) != NULL) { + // + // If provided at the command line, set the resumption ticket that can + // be used to resume a previous session. + // + uint8_t ResumptionTicket[10240]; + uint16_t TicketLength = (uint16_t)DecodeHexBuffer(ResumptionTicketString, sizeof(ResumptionTicket), ResumptionTicket); + if (QUIC_FAILED(Status = MsQuic->SetParam(Connection, QUIC_PARAM_CONN_RESUMPTION_TICKET, TicketLength, ResumptionTicket))) { + printf("SetParam(QUIC_PARAM_CONN_RESUMPTION_TICKET) failed, 0x%x!\n", Status); + goto Error; + } + } + + // + // Get the target / server name or IP from the command line. + // + const char* Target; + if ((Target = GetValue(argc, argv, "target")) == NULL) { + printf("Must specify '-target' argument!\n"); + Status = QUIC_STATUS_INVALID_PARAMETER; + goto Error; + } + + printf("[conn][%p] Connecting...\n", Connection); + + // + // Start the connection to the server. + // + if (QUIC_FAILED(Status = MsQuic->ConnectionStart(Connection, Configuration, QUIC_ADDRESS_FAMILY_UNSPEC, Target, UdpPort))) { + printf("ConnectionStart failed, 0x%x!\n", Status); + goto Error; + } + + Error: + + if (QUIC_FAILED(Status) && Connection != NULL) { + MsQuic->ConnectionClose(Connection); + } +} + +int +QUIC_MAIN_EXPORT +main( + _In_ int argc, + _In_reads_(argc) _Null_terminated_ char* argv[] +) +{ + QUIC_STATUS Status = QUIC_STATUS_SUCCESS; + + // + // Open a handle to the library and get the API function table. + // + if (QUIC_FAILED(Status = MsQuicOpen2(&MsQuic))) { + printf("MsQuicOpen2 failed, 0x%x!\n", Status); + goto Error; + } + + // + // Create a registration for the app's connections. + // + if (QUIC_FAILED(Status = MsQuic->RegistrationOpen(&RegConfig, &Registration))) { + printf("RegistrationOpen failed, 0x%x!\n", Status); + goto Error; + } + + if (GetFlag(argc, argv, "help") || GetFlag(argc, argv, "?")) { + PrintUsage(); + } else if (GetFlag(argc, argv, "client")) { + RunClient(argc, argv); + } else if (GetFlag(argc, argv, "server")) { + RunServer(argc, argv); + } else { + PrintUsage(); + } + + Error: + + if (MsQuic != NULL) { + if (Configuration != NULL) { + MsQuic->ConfigurationClose(Configuration); + } + if (Registration != NULL) { + // + // This will block until all outstanding child objects have been + // closed. + // + MsQuic->RegistrationClose(Registration); + } + MsQuicClose(MsQuic); + } + + return (int)Status; +} \ No newline at end of file diff --git a/msquic/LICENSE b/msquic/LICENSE new file mode 100644 index 0000000..63447fd --- /dev/null +++ b/msquic/LICENSE @@ -0,0 +1,21 @@ +Copyright (c) Microsoft Corporation. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/msquic/THIRD-PARTY-NOTICES b/msquic/THIRD-PARTY-NOTICES new file mode 100644 index 0000000..274f37d --- /dev/null +++ b/msquic/THIRD-PARTY-NOTICES @@ -0,0 +1,187 @@ +In some configuration, MsQuic uses third-party libraries or other resources +that may be distributed under licenses different than the MsQuic software. + +In the event that we accidentally failed to list a required notice, please +bring it to our attention by posting a GitHub issue or Discussion item. + +The attached notices are provided for information only. + +License notice for OpenSSL +------------------------------- + + Apache License + Version 2.0, January 2004 + https://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/msquic/include/msquic.h b/msquic/include/msquic.h new file mode 100644 index 0000000..be82221 --- /dev/null +++ b/msquic/include/msquic.h @@ -0,0 +1,1640 @@ +/*++ + + Copyright (c) Microsoft Corporation. + Licensed under the MIT License. + +Abstract: + + Declarations for the MsQuic API, which enables applications and drivers to + create QUIC connections as a client or server. + + For more detailed information, see ../docs/API.md + +Supported Platforms: + + Windows User mode + Windows Kernel mode + Linux User mode + +--*/ + +#ifndef _MSQUIC_ +#define _MSQUIC_ + +#ifdef _WIN32 +#pragma once +#endif + +#pragma warning(disable:4201) // nonstandard extension used: nameless struct/union +#pragma warning(disable:4214) // nonstandard extension used: bit field types other than int + +#ifdef _KERNEL_MODE +#include "msquic_winkernel.h" +#elif _WIN32 +#include "msquic_winuser.h" +#elif __linux__ || __APPLE__ || __FreeBSD__ +#include "msquic_posix.h" +#else +#error "Unsupported Platform" +#endif + +#if defined(__cplusplus) +extern "C" { +#endif + +typedef struct QUIC_HANDLE *HQUIC; + +// +// The maximum value that can be encoded in a 62-bit integer. +// +#define QUIC_UINT62_MAX ((1ULL << 62U) - 1) + +// +// Represents a 62-bit integer. +// +typedef _In_range_(0, QUIC_UINT62_MAX) uint64_t QUIC_UINT62; + +// +// An ALPN must not exceed 255 bytes, and must not be zero-length. +// +#define QUIC_MAX_ALPN_LENGTH 255 + +// +// A server name must not exceed 65535 bytes. +// +#define QUIC_MAX_SNI_LENGTH 65535 + +// +// The maximum number of bytes of application data a server application can +// send in a resumption ticket. +// +#define QUIC_MAX_RESUMPTION_APP_DATA_LENGTH 1000 + +typedef enum QUIC_TLS_PROVIDER { + QUIC_TLS_PROVIDER_SCHANNEL = 0x0000, + QUIC_TLS_PROVIDER_OPENSSL = 0x0001, +} QUIC_TLS_PROVIDER; + +typedef enum QUIC_EXECUTION_PROFILE { + QUIC_EXECUTION_PROFILE_LOW_LATENCY, // Default + QUIC_EXECUTION_PROFILE_TYPE_MAX_THROUGHPUT, + QUIC_EXECUTION_PROFILE_TYPE_SCAVENGER, + QUIC_EXECUTION_PROFILE_TYPE_REAL_TIME, +} QUIC_EXECUTION_PROFILE; + +typedef enum QUIC_LOAD_BALANCING_MODE { + QUIC_LOAD_BALANCING_DISABLED, // Default + QUIC_LOAD_BALANCING_SERVER_ID_IP, // Encodes IP address in Server ID + QUIC_LOAD_BALANCING_SERVER_ID_FIXED, // Encodes a fixed 4-byte value in Server ID + QUIC_LOAD_BALANCING_COUNT, // The number of supported load balancing modes + // MUST BE LAST +} QUIC_LOAD_BALANCING_MODE; + +typedef enum QUIC_TLS_ALERT_CODES { + QUIC_TLS_ALERT_CODE_SUCCESS = 0xFFFF, // Not a real TlsAlert + QUIC_TLS_ALERT_CODE_UNEXPECTED_MESSAGE = 10, + QUIC_TLS_ALERT_CODE_BAD_CERTIFICATE = 42, + QUIC_TLS_ALERT_CODE_UNSUPPORTED_CERTIFICATE = 43, + QUIC_TLS_ALERT_CODE_CERTIFICATE_REVOKED = 44, + QUIC_TLS_ALERT_CODE_CERTIFICATE_EXPIRED = 45, + QUIC_TLS_ALERT_CODE_CERTIFICATE_UNKNOWN = 46, + QUIC_TLS_ALERT_CODE_ILLEGAL_PARAMETER = 47, + QUIC_TLS_ALERT_CODE_UNKNOWN_CA = 48, + QUIC_TLS_ALERT_CODE_ACCESS_DENIED = 49, + QUIC_TLS_ALERT_CODE_INSUFFICIENT_SECURITY = 71, + QUIC_TLS_ALERT_CODE_INTERNAL_ERROR = 80, + QUIC_TLS_ALERT_CODE_USER_CANCELED = 90, + QUIC_TLS_ALERT_CODE_CERTIFICATE_REQUIRED = 116, + QUIC_TLS_ALERT_CODE_MAX = 255, +} QUIC_TLS_ALERT_CODES; + +typedef enum QUIC_CREDENTIAL_TYPE { + QUIC_CREDENTIAL_TYPE_NONE, + QUIC_CREDENTIAL_TYPE_CERTIFICATE_HASH, + QUIC_CREDENTIAL_TYPE_CERTIFICATE_HASH_STORE, + QUIC_CREDENTIAL_TYPE_CERTIFICATE_CONTEXT, + QUIC_CREDENTIAL_TYPE_CERTIFICATE_FILE, + QUIC_CREDENTIAL_TYPE_CERTIFICATE_FILE_PROTECTED, + QUIC_CREDENTIAL_TYPE_CERTIFICATE_PKCS12, +} QUIC_CREDENTIAL_TYPE; + +typedef enum QUIC_CREDENTIAL_FLAGS { + QUIC_CREDENTIAL_FLAG_NONE = 0x00000000, + QUIC_CREDENTIAL_FLAG_CLIENT = 0x00000001, // Lack of client flag indicates server. + QUIC_CREDENTIAL_FLAG_LOAD_ASYNCHRONOUS = 0x00000002, + QUIC_CREDENTIAL_FLAG_NO_CERTIFICATE_VALIDATION = 0x00000004, + QUIC_CREDENTIAL_FLAG_ENABLE_OCSP = 0x00000008, // Schannel only currently + QUIC_CREDENTIAL_FLAG_INDICATE_CERTIFICATE_RECEIVED = 0x00000010, + QUIC_CREDENTIAL_FLAG_DEFER_CERTIFICATE_VALIDATION = 0x00000020, + QUIC_CREDENTIAL_FLAG_REQUIRE_CLIENT_AUTHENTICATION = 0x00000040, + QUIC_CREDENTIAL_FLAG_USE_TLS_BUILTIN_CERTIFICATE_VALIDATION = 0x00000080, // OpenSSL only currently + QUIC_CREDENTIAL_FLAG_REVOCATION_CHECK_END_CERT = 0x00000100, // Schannel only currently + QUIC_CREDENTIAL_FLAG_REVOCATION_CHECK_CHAIN = 0x00000200, // Schannel only currently + QUIC_CREDENTIAL_FLAG_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT = 0x00000400, // Schannel only currently + QUIC_CREDENTIAL_FLAG_IGNORE_NO_REVOCATION_CHECK = 0x00000800, // Schannel only currently + QUIC_CREDENTIAL_FLAG_IGNORE_REVOCATION_OFFLINE = 0x00001000, // Schannel only currently + QUIC_CREDENTIAL_FLAG_SET_ALLOWED_CIPHER_SUITES = 0x00002000, + QUIC_CREDENTIAL_FLAG_USE_PORTABLE_CERTIFICATES = 0x00004000, + QUIC_CREDENTIAL_FLAG_USE_SUPPLIED_CREDENTIALS = 0x00008000, // Schannel only + QUIC_CREDENTIAL_FLAG_USE_SYSTEM_MAPPER = 0x00010000, // Schannel only + QUIC_CREDENTIAL_FLAG_CACHE_ONLY_URL_RETRIEVAL = 0x00020000, // Windows only currently + QUIC_CREDENTIAL_FLAG_REVOCATION_CHECK_CACHE_ONLY = 0x00040000, // Windows only currently + QUIC_CREDENTIAL_FLAG_INPROC_PEER_CERTIFICATE = 0x00080000, // Schannel only + QUIC_CREDENTIAL_FLAG_SET_CA_CERTIFICATE_FILE = 0x00100000, // OpenSSL only currently +} QUIC_CREDENTIAL_FLAGS; + +DEFINE_ENUM_FLAG_OPERATORS(QUIC_CREDENTIAL_FLAGS) + +typedef enum QUIC_ALLOWED_CIPHER_SUITE_FLAGS { + QUIC_ALLOWED_CIPHER_SUITE_NONE = 0x0, + QUIC_ALLOWED_CIPHER_SUITE_AES_128_GCM_SHA256 = 0x1, + QUIC_ALLOWED_CIPHER_SUITE_AES_256_GCM_SHA384 = 0x2, + QUIC_ALLOWED_CIPHER_SUITE_CHACHA20_POLY1305_SHA256 = 0x4, // Not supported on Schannel +} QUIC_ALLOWED_CIPHER_SUITE_FLAGS; + +DEFINE_ENUM_FLAG_OPERATORS(QUIC_ALLOWED_CIPHER_SUITE_FLAGS); + +typedef enum QUIC_CERTIFICATE_HASH_STORE_FLAGS { + QUIC_CERTIFICATE_HASH_STORE_FLAG_NONE = 0x0000, + QUIC_CERTIFICATE_HASH_STORE_FLAG_MACHINE_STORE = 0x0001, +} QUIC_CERTIFICATE_HASH_STORE_FLAGS; + +DEFINE_ENUM_FLAG_OPERATORS(QUIC_CERTIFICATE_HASH_STORE_FLAGS) + +typedef enum QUIC_CONNECTION_SHUTDOWN_FLAGS { + QUIC_CONNECTION_SHUTDOWN_FLAG_NONE = 0x0000, + QUIC_CONNECTION_SHUTDOWN_FLAG_SILENT = 0x0001, // Don't send the close frame over the network. +} QUIC_CONNECTION_SHUTDOWN_FLAGS; + +DEFINE_ENUM_FLAG_OPERATORS(QUIC_CONNECTION_SHUTDOWN_FLAGS) + +typedef enum QUIC_SERVER_RESUMPTION_LEVEL { + QUIC_SERVER_NO_RESUME, + QUIC_SERVER_RESUME_ONLY, + QUIC_SERVER_RESUME_AND_ZERORTT, +} QUIC_SERVER_RESUMPTION_LEVEL; + +typedef enum QUIC_SEND_RESUMPTION_FLAGS { + QUIC_SEND_RESUMPTION_FLAG_NONE = 0x0000, + QUIC_SEND_RESUMPTION_FLAG_FINAL = 0x0001, // Free TLS state after sending this ticket. +} QUIC_SEND_RESUMPTION_FLAGS; + +DEFINE_ENUM_FLAG_OPERATORS(QUIC_SEND_RESUMPTION_FLAGS) + +typedef enum QUIC_STREAM_SCHEDULING_SCHEME { + QUIC_STREAM_SCHEDULING_SCHEME_FIFO = 0x0000, // Sends stream data first come, first served. (Default) + QUIC_STREAM_SCHEDULING_SCHEME_ROUND_ROBIN = 0x0001, // Sends stream data evenly multiplexed. + QUIC_STREAM_SCHEDULING_SCHEME_COUNT, // The number of stream scheduling schemes. +} QUIC_STREAM_SCHEDULING_SCHEME; + +typedef enum QUIC_STREAM_OPEN_FLAGS { + QUIC_STREAM_OPEN_FLAG_NONE = 0x0000, + QUIC_STREAM_OPEN_FLAG_UNIDIRECTIONAL = 0x0001, // Indicates the stream is unidirectional. + QUIC_STREAM_OPEN_FLAG_0_RTT = 0x0002, // The stream was opened via a 0-RTT packet. + QUIC_STREAM_OPEN_FLAG_DELAY_ID_FC_UPDATES = 0x0004, // Indicates stream ID flow control limit updates for the + // connection should be delayed to StreamClose. +} QUIC_STREAM_OPEN_FLAGS; + +DEFINE_ENUM_FLAG_OPERATORS(QUIC_STREAM_OPEN_FLAGS) + +typedef enum QUIC_STREAM_START_FLAGS { + QUIC_STREAM_START_FLAG_NONE = 0x0000, + QUIC_STREAM_START_FLAG_IMMEDIATE = 0x0001, // Immediately informs peer that stream is open. + QUIC_STREAM_START_FLAG_FAIL_BLOCKED = 0x0002, // Only opens the stream if flow control allows. + QUIC_STREAM_START_FLAG_SHUTDOWN_ON_FAIL = 0x0004, // Shutdown the stream immediately after start failure. + QUIC_STREAM_START_FLAG_INDICATE_PEER_ACCEPT = 0x0008, // Indicate PEER_ACCEPTED event if not accepted at start. +} QUIC_STREAM_START_FLAGS; + +DEFINE_ENUM_FLAG_OPERATORS(QUIC_STREAM_START_FLAGS) + +typedef enum QUIC_STREAM_SHUTDOWN_FLAGS { + QUIC_STREAM_SHUTDOWN_FLAG_NONE = 0x0000, + QUIC_STREAM_SHUTDOWN_FLAG_GRACEFUL = 0x0001, // Cleanly closes the send path. + QUIC_STREAM_SHUTDOWN_FLAG_ABORT_SEND = 0x0002, // Abruptly closes the send path. + QUIC_STREAM_SHUTDOWN_FLAG_ABORT_RECEIVE = 0x0004, // Abruptly closes the receive path. + QUIC_STREAM_SHUTDOWN_FLAG_ABORT = 0x0006, // Abruptly closes both send and receive paths. + QUIC_STREAM_SHUTDOWN_FLAG_IMMEDIATE = 0x0008, // Immediately sends completion events to app. + QUIC_STREAM_SHUTDOWN_FLAG_INLINE = 0x0010, // Process the shutdown immediately inline. Only for calls on callbacks. + // WARNING: Can cause reentrant callbacks! +} QUIC_STREAM_SHUTDOWN_FLAGS; + +DEFINE_ENUM_FLAG_OPERATORS(QUIC_STREAM_SHUTDOWN_FLAGS) + +typedef enum QUIC_RECEIVE_FLAGS { + QUIC_RECEIVE_FLAG_NONE = 0x0000, + QUIC_RECEIVE_FLAG_0_RTT = 0x0001, // Data was encrypted with 0-RTT key. + QUIC_RECEIVE_FLAG_FIN = 0x0002, // FIN was included with this data. +} QUIC_RECEIVE_FLAGS; + +DEFINE_ENUM_FLAG_OPERATORS(QUIC_RECEIVE_FLAGS) + +typedef enum QUIC_SEND_FLAGS { + QUIC_SEND_FLAG_NONE = 0x0000, + QUIC_SEND_FLAG_ALLOW_0_RTT = 0x0001, // Allows the use of encrypting with 0-RTT key. + QUIC_SEND_FLAG_START = 0x0002, // Asynchronously starts the stream with the sent data. + QUIC_SEND_FLAG_FIN = 0x0004, // Indicates the request is the one last sent on the stream. + QUIC_SEND_FLAG_DGRAM_PRIORITY = 0x0008, // Indicates the datagram is higher priority than others. + QUIC_SEND_FLAG_DELAY_SEND = 0x0010, // Indicates the send should be delayed because more will be queued soon. +} QUIC_SEND_FLAGS; + +DEFINE_ENUM_FLAG_OPERATORS(QUIC_SEND_FLAGS) + +typedef enum QUIC_DATAGRAM_SEND_STATE { + QUIC_DATAGRAM_SEND_UNKNOWN, // Not yet sent. + QUIC_DATAGRAM_SEND_SENT, // Sent and awaiting acknowledegment + QUIC_DATAGRAM_SEND_LOST_SUSPECT, // Suspected as lost, but still tracked + QUIC_DATAGRAM_SEND_LOST_DISCARDED, // Lost and not longer being tracked + QUIC_DATAGRAM_SEND_ACKNOWLEDGED, // Acknowledged + QUIC_DATAGRAM_SEND_ACKNOWLEDGED_SPURIOUS, // Acknowledged after being suspected lost + QUIC_DATAGRAM_SEND_CANCELED, // Canceled before send +} QUIC_DATAGRAM_SEND_STATE; + +// +// Helper to determine if a datagrams state is final, and no longer tracked +// by MsQuic. +// +#define QUIC_DATAGRAM_SEND_STATE_IS_FINAL(State) \ + ((State) >= QUIC_DATAGRAM_SEND_LOST_DISCARDED) + +typedef enum QUIC_EXECUTION_CONFIG_FLAGS { + QUIC_EXECUTION_CONFIG_FLAG_NONE = 0x0000, +#ifdef QUIC_API_ENABLE_PREVIEW_FEATURES + QUIC_EXECUTION_CONFIG_FLAG_QTIP = 0x0001, + QUIC_EXECUTION_CONFIG_FLAG_RIO = 0x0002, +#endif +} QUIC_EXECUTION_CONFIG_FLAGS; + +DEFINE_ENUM_FLAG_OPERATORS(QUIC_EXECUTION_CONFIG_FLAGS) + +// +// A custom configuration for thread execution in QUIC. +// +typedef struct QUIC_EXECUTION_CONFIG { + + QUIC_EXECUTION_CONFIG_FLAGS Flags; + uint32_t PollingIdleTimeoutUs; // Time before a polling thread, with no work to do, sleeps. + uint32_t ProcessorCount; + _Field_size_(ProcessorCount) + uint16_t ProcessorList[1]; // List of processors to use for threads. + +} QUIC_EXECUTION_CONFIG; + +#define QUIC_EXECUTION_CONFIG_MIN_SIZE \ + (uint32_t)FIELD_OFFSET(QUIC_EXECUTION_CONFIG, ProcessorList) + +typedef struct QUIC_REGISTRATION_CONFIG { // All fields may be NULL/zero. + const char* AppName; + QUIC_EXECUTION_PROFILE ExecutionProfile; +} QUIC_REGISTRATION_CONFIG; + +typedef +_IRQL_requires_max_(PASSIVE_LEVEL) +_Function_class_(QUIC_CREDENTIAL_LOAD_COMPLETE) +void +(QUIC_API QUIC_CREDENTIAL_LOAD_COMPLETE)( + _In_ HQUIC Configuration, + _In_opt_ void* Context, + _In_ QUIC_STATUS Status + ); + +typedef QUIC_CREDENTIAL_LOAD_COMPLETE *QUIC_CREDENTIAL_LOAD_COMPLETE_HANDLER; + +typedef struct QUIC_CERTIFICATE_HASH { + uint8_t ShaHash[20]; +} QUIC_CERTIFICATE_HASH; + +typedef struct QUIC_CERTIFICATE_HASH_STORE { + QUIC_CERTIFICATE_HASH_STORE_FLAGS Flags; + uint8_t ShaHash[20]; + char StoreName[128]; +} QUIC_CERTIFICATE_HASH_STORE; + +typedef struct QUIC_CERTIFICATE_FILE { + const char *PrivateKeyFile; + const char *CertificateFile; +} QUIC_CERTIFICATE_FILE; + +typedef struct QUIC_CERTIFICATE_FILE_PROTECTED { + const char *PrivateKeyFile; + const char *CertificateFile; + const char *PrivateKeyPassword; +} QUIC_CERTIFICATE_FILE_PROTECTED; + +typedef struct QUIC_CERTIFICATE_PKCS12 { + const uint8_t *Asn1Blob; + uint32_t Asn1BlobLength; + const char *PrivateKeyPassword; // Optional: used if provided. Ignored if NULL +} QUIC_CERTIFICATE_PKCS12; + +typedef void QUIC_CERTIFICATE; // Platform specific certificate object +typedef void QUIC_CERTIFICATE_CHAIN; // Platform specific certificate chain object + +typedef struct QUIC_CREDENTIAL_CONFIG { + QUIC_CREDENTIAL_TYPE Type; + QUIC_CREDENTIAL_FLAGS Flags; + union { + QUIC_CERTIFICATE_HASH* CertificateHash; + QUIC_CERTIFICATE_HASH_STORE* CertificateHashStore; + QUIC_CERTIFICATE* CertificateContext; + QUIC_CERTIFICATE_FILE* CertificateFile; + QUIC_CERTIFICATE_FILE_PROTECTED* CertificateFileProtected; + QUIC_CERTIFICATE_PKCS12* CertificatePkcs12; + }; + const char* Principal; + void* Reserved; // Currently unused + QUIC_CREDENTIAL_LOAD_COMPLETE_HANDLER AsyncHandler; // Optional + QUIC_ALLOWED_CIPHER_SUITE_FLAGS AllowedCipherSuites;// Optional + const char* CaCertificateFile; // Optional +} QUIC_CREDENTIAL_CONFIG; + +// +// The maximum number of QUIC_TICKET_KEY_CONFIG that can be used at one time. +// +#define QUIC_MAX_TICKET_KEY_COUNT 16 + +// +// TLS New Session Ticket encryption key configuration. +// +typedef struct QUIC_TICKET_KEY_CONFIG { + uint8_t Id[16]; + uint8_t Material[64]; + uint8_t MaterialLength; +} QUIC_TICKET_KEY_CONFIG; + +// +// A single contiguous buffer. +// +typedef struct QUIC_BUFFER { + uint32_t Length; + _Field_size_bytes_(Length) + uint8_t* Buffer; +} QUIC_BUFFER; + +// +// All the available information describing a new incoming connection. +// +typedef struct QUIC_NEW_CONNECTION_INFO { + uint32_t QuicVersion; + const QUIC_ADDR* LocalAddress; + const QUIC_ADDR* RemoteAddress; + uint32_t CryptoBufferLength; + uint16_t ClientAlpnListLength; + uint16_t ServerNameLength; + uint8_t NegotiatedAlpnLength; + _Field_size_bytes_(CryptoBufferLength) + const uint8_t* CryptoBuffer; + _Field_size_bytes_(ClientAlpnListLength) + const uint8_t* ClientAlpnList; + _Field_size_bytes_(NegotiatedAlpnLength) + const uint8_t* NegotiatedAlpn; + _Field_size_bytes_opt_(ServerNameLength) + const char* ServerName; +} QUIC_NEW_CONNECTION_INFO; + +typedef enum QUIC_TLS_PROTOCOL_VERSION { + QUIC_TLS_PROTOCOL_UNKNOWN = 0, + QUIC_TLS_PROTOCOL_1_3 = 0x3000, +} QUIC_TLS_PROTOCOL_VERSION; + +typedef enum QUIC_CIPHER_ALGORITHM { + QUIC_CIPHER_ALGORITHM_NONE = 0, + QUIC_CIPHER_ALGORITHM_AES_128 = 0x660E, + QUIC_CIPHER_ALGORITHM_AES_256 = 0x6610, + QUIC_CIPHER_ALGORITHM_CHACHA20 = 0x6612, // Not supported on Schannel/BCrypt +} QUIC_CIPHER_ALGORITHM; + +typedef enum QUIC_HASH_ALGORITHM { + QUIC_HASH_ALGORITHM_NONE = 0, + QUIC_HASH_ALGORITHM_SHA_256 = 0x800C, + QUIC_HASH_ALGORITHM_SHA_384 = 0x800D, +} QUIC_HASH_ALGORITHM; + +typedef enum QUIC_KEY_EXCHANGE_ALGORITHM { + QUIC_KEY_EXCHANGE_ALGORITHM_NONE = 0, +} QUIC_KEY_EXCHANGE_ALGORITHM; + +typedef enum QUIC_CIPHER_SUITE { + QUIC_CIPHER_SUITE_TLS_AES_128_GCM_SHA256 = 0x1301, + QUIC_CIPHER_SUITE_TLS_AES_256_GCM_SHA384 = 0x1302, + QUIC_CIPHER_SUITE_TLS_CHACHA20_POLY1305_SHA256 = 0x1303, // Not supported on Schannel +} QUIC_CIPHER_SUITE; + +typedef enum QUIC_CONGESTION_CONTROL_ALGORITHM { + QUIC_CONGESTION_CONTROL_ALGORITHM_CUBIC, +#ifdef QUIC_API_ENABLE_PREVIEW_FEATURES + QUIC_CONGESTION_CONTROL_ALGORITHM_BBR, +#endif + QUIC_CONGESTION_CONTROL_ALGORITHM_MAX, +} QUIC_CONGESTION_CONTROL_ALGORITHM; + +// +// All the available information describing a handshake. +// +typedef struct QUIC_HANDSHAKE_INFO { + QUIC_TLS_PROTOCOL_VERSION TlsProtocolVersion; + QUIC_CIPHER_ALGORITHM CipherAlgorithm; + int32_t CipherStrength; + QUIC_HASH_ALGORITHM Hash; + int32_t HashStrength; + QUIC_KEY_EXCHANGE_ALGORITHM KeyExchangeAlgorithm; + int32_t KeyExchangeStrength; + QUIC_CIPHER_SUITE CipherSuite; +} QUIC_HANDSHAKE_INFO; + +// +// All statistics available to query about a connection. +// +typedef struct QUIC_STATISTICS { + uint64_t CorrelationId; + uint32_t VersionNegotiation : 1; + uint32_t StatelessRetry : 1; + uint32_t ResumptionAttempted : 1; + uint32_t ResumptionSucceeded : 1; + uint32_t Rtt; // In microseconds + uint32_t MinRtt; // In microseconds + uint32_t MaxRtt; // In microseconds + struct { + uint64_t Start; + uint64_t InitialFlightEnd; // Processed all peer's Initial packets + uint64_t HandshakeFlightEnd; // Processed all peer's Handshake packets + } Timing; + struct { + uint32_t ClientFlight1Bytes; // Sum of TLS payloads + uint32_t ServerFlight1Bytes; // Sum of TLS payloads + uint32_t ClientFlight2Bytes; // Sum of TLS payloads + } Handshake; + struct { + uint16_t PathMtu; // Current path MTU. + uint64_t TotalPackets; // QUIC packets; could be coalesced into fewer UDP datagrams. + uint64_t RetransmittablePackets; + uint64_t SuspectedLostPackets; + uint64_t SpuriousLostPackets; // Actual lost is (SuspectedLostPackets - SpuriousLostPackets) + uint64_t TotalBytes; // Sum of UDP payloads + uint64_t TotalStreamBytes; // Sum of stream payloads + uint32_t CongestionCount; // Number of congestion events + uint32_t PersistentCongestionCount; // Number of persistent congestion events + } Send; + struct { + uint64_t TotalPackets; // QUIC packets; could be coalesced into fewer UDP datagrams. + uint64_t ReorderedPackets; // Packets where packet number is less than highest seen. + uint64_t DroppedPackets; // Includes DuplicatePackets. + uint64_t DuplicatePackets; + uint64_t TotalBytes; // Sum of UDP payloads + uint64_t TotalStreamBytes; // Sum of stream payloads + uint64_t DecryptionFailures; // Count of packet decryption failures. + uint64_t ValidAckFrames; // Count of receive ACK frames. + } Recv; + struct { + uint32_t KeyUpdateCount; + } Misc; +} QUIC_STATISTICS; + +// +// N.B. Consumers of this struct depend on it being the same for 32-bit and +// 64-bit systems. DO NOT include any fields that have different sizes on those +// platforms, such as size_t or pointers. +// +typedef struct QUIC_STATISTICS_V2 { + + uint64_t CorrelationId; + uint32_t VersionNegotiation : 1; + uint32_t StatelessRetry : 1; + uint32_t ResumptionAttempted : 1; + uint32_t ResumptionSucceeded : 1; + uint32_t GreaseBitNegotiated : 1; // Set if we negotiated the GREASE bit. + uint32_t EcnCapable : 1; + uint32_t RESERVED : 26; + uint32_t Rtt; // In microseconds + uint32_t MinRtt; // In microseconds + uint32_t MaxRtt; // In microseconds + + uint64_t TimingStart; + uint64_t TimingInitialFlightEnd; // Processed all peer's Initial packets + uint64_t TimingHandshakeFlightEnd; // Processed all peer's Handshake packets + + uint32_t HandshakeClientFlight1Bytes; // Sum of TLS payloads + uint32_t HandshakeServerFlight1Bytes; // Sum of TLS payloads + uint32_t HandshakeClientFlight2Bytes; // Sum of TLS payloads + + uint16_t SendPathMtu; // Current path MTU. + uint64_t SendTotalPackets; // QUIC packets; could be coalesced into fewer UDP datagrams. + uint64_t SendRetransmittablePackets; + uint64_t SendSuspectedLostPackets; + uint64_t SendSpuriousLostPackets; // Actual lost is (SuspectedLostPackets - SpuriousLostPackets) + uint64_t SendTotalBytes; // Sum of UDP payloads + uint64_t SendTotalStreamBytes; // Sum of stream payloads + uint32_t SendCongestionCount; // Number of congestion events + uint32_t SendPersistentCongestionCount; // Number of persistent congestion events + + uint64_t RecvTotalPackets; // QUIC packets; could be coalesced into fewer UDP datagrams. + uint64_t RecvReorderedPackets; // Packets where packet number is less than highest seen. + uint64_t RecvDroppedPackets; // Includes DuplicatePackets. + uint64_t RecvDuplicatePackets; + uint64_t RecvTotalBytes; // Sum of UDP payloads + uint64_t RecvTotalStreamBytes; // Sum of stream payloads + uint64_t RecvDecryptionFailures; // Count of packet decryption failures. + uint64_t RecvValidAckFrames; // Count of receive ACK frames. + + uint32_t KeyUpdateCount; + + uint32_t SendCongestionWindow; // Congestion window size + + uint32_t DestCidUpdateCount; // Number of times the destionation CID changed. + + uint32_t SendEcnCongestionCount; // Number of congestion events caused by ECN. + + // N.B. New fields must be appended to end + +} QUIC_STATISTICS_V2; + +#define QUIC_STRUCT_SIZE_THRU_FIELD(Struct, Field) \ + (FIELD_OFFSET(Struct, Field) + sizeof(((Struct*)0)->Field)) + +#define QUIC_STATISTICS_V2_SIZE_1 QUIC_STRUCT_SIZE_THRU_FIELD(QUIC_STATISTICS_V2, KeyUpdateCount) // v2.0 final size +#define QUIC_STATISTICS_V2_SIZE_2 QUIC_STRUCT_SIZE_THRU_FIELD(QUIC_STATISTICS_V2, DestCidUpdateCount) // v2.1 final size +#define QUIC_STATISTICS_V2_SIZE_3 QUIC_STRUCT_SIZE_THRU_FIELD(QUIC_STATISTICS_V2, SendEcnCongestionCount) // v2.2 final size + +typedef struct QUIC_LISTENER_STATISTICS { + + uint64_t TotalAcceptedConnections; + uint64_t TotalRejectedConnections; + + uint64_t BindingRecvDroppedPackets; + +} QUIC_LISTENER_STATISTICS; + +typedef enum QUIC_PERFORMANCE_COUNTERS { + QUIC_PERF_COUNTER_CONN_CREATED, // Total connections ever allocated. + QUIC_PERF_COUNTER_CONN_HANDSHAKE_FAIL, // Total connections that failed during handshake. + QUIC_PERF_COUNTER_CONN_APP_REJECT, // Total connections rejected by the application. + QUIC_PERF_COUNTER_CONN_RESUMED, // Total connections resumed. + QUIC_PERF_COUNTER_CONN_ACTIVE, // Connections currently allocated. + QUIC_PERF_COUNTER_CONN_CONNECTED, // Connections currently in the connected state. + QUIC_PERF_COUNTER_CONN_PROTOCOL_ERRORS, // Total connections shutdown with a protocol error. + QUIC_PERF_COUNTER_CONN_NO_ALPN, // Total connection attempts with no matching ALPN. + QUIC_PERF_COUNTER_STRM_ACTIVE, // Current streams allocated. + QUIC_PERF_COUNTER_PKTS_SUSPECTED_LOST, // Total suspected packets lost + QUIC_PERF_COUNTER_PKTS_DROPPED, // Total packets dropped for any reason. + QUIC_PERF_COUNTER_PKTS_DECRYPTION_FAIL, // Total packets with decryption failures. + QUIC_PERF_COUNTER_UDP_RECV, // Total UDP datagrams received. + QUIC_PERF_COUNTER_UDP_SEND, // Total UDP datagrams sent. + QUIC_PERF_COUNTER_UDP_RECV_BYTES, // Total UDP payload bytes received. + QUIC_PERF_COUNTER_UDP_SEND_BYTES, // Total UDP payload bytes sent. + QUIC_PERF_COUNTER_UDP_RECV_EVENTS, // Total UDP receive events. + QUIC_PERF_COUNTER_UDP_SEND_CALLS, // Total UDP send API calls. + QUIC_PERF_COUNTER_APP_SEND_BYTES, // Total bytes sent by applications. + QUIC_PERF_COUNTER_APP_RECV_BYTES, // Total bytes received by applications. + QUIC_PERF_COUNTER_CONN_QUEUE_DEPTH, // Current connections queued for processing. + QUIC_PERF_COUNTER_CONN_OPER_QUEUE_DEPTH,// Current connection operations queued. + QUIC_PERF_COUNTER_CONN_OPER_QUEUED, // Total connection operations queued ever. + QUIC_PERF_COUNTER_CONN_OPER_COMPLETED, // Total connection operations processed ever. + QUIC_PERF_COUNTER_WORK_OPER_QUEUE_DEPTH,// Current worker operations queued. + QUIC_PERF_COUNTER_WORK_OPER_QUEUED, // Total worker operations queued ever. + QUIC_PERF_COUNTER_WORK_OPER_COMPLETED, // Total worker operations processed ever. + QUIC_PERF_COUNTER_PATH_VALIDATED, // Total path challenges that succeed ever. + QUIC_PERF_COUNTER_PATH_FAILURE, // Total path challenges that fail ever. + QUIC_PERF_COUNTER_SEND_STATELESS_RESET, // Total stateless reset packets sent ever. + QUIC_PERF_COUNTER_SEND_STATELESS_RETRY, // Total stateless retry packets sent ever. + QUIC_PERF_COUNTER_MAX, +} QUIC_PERFORMANCE_COUNTERS; + +#ifdef QUIC_API_ENABLE_PREVIEW_FEATURES +typedef struct QUIC_VERSION_SETTINGS { + + const uint32_t* AcceptableVersions; + const uint32_t* OfferedVersions; + const uint32_t* FullyDeployedVersions; + uint32_t AcceptableVersionsLength; + uint32_t OfferedVersionsLength; + uint32_t FullyDeployedVersionsLength; + +} QUIC_VERSION_SETTINGS; +#endif + +typedef struct QUIC_GLOBAL_SETTINGS { + union { + uint64_t IsSetFlags; + struct { + uint64_t RetryMemoryLimit : 1; + uint64_t LoadBalancingMode : 1; + uint64_t FixedServerID : 1; + uint64_t RESERVED : 61; + } IsSet; + }; + uint16_t RetryMemoryLimit; + uint16_t LoadBalancingMode; + uint32_t FixedServerID; +} QUIC_GLOBAL_SETTINGS; + +typedef struct QUIC_SETTINGS { + + union { + uint64_t IsSetFlags; + struct { + uint64_t MaxBytesPerKey : 1; + uint64_t HandshakeIdleTimeoutMs : 1; + uint64_t IdleTimeoutMs : 1; + uint64_t MtuDiscoverySearchCompleteTimeoutUs : 1; + uint64_t TlsClientMaxSendBuffer : 1; + uint64_t TlsServerMaxSendBuffer : 1; + uint64_t StreamRecvWindowDefault : 1; + uint64_t StreamRecvBufferDefault : 1; + uint64_t ConnFlowControlWindow : 1; + uint64_t MaxWorkerQueueDelayUs : 1; + uint64_t MaxStatelessOperations : 1; + uint64_t InitialWindowPackets : 1; + uint64_t SendIdleTimeoutMs : 1; + uint64_t InitialRttMs : 1; + uint64_t MaxAckDelayMs : 1; + uint64_t DisconnectTimeoutMs : 1; + uint64_t KeepAliveIntervalMs : 1; + uint64_t CongestionControlAlgorithm : 1; + uint64_t PeerBidiStreamCount : 1; + uint64_t PeerUnidiStreamCount : 1; + uint64_t MaxBindingStatelessOperations : 1; + uint64_t StatelessOperationExpirationMs : 1; + uint64_t MinimumMtu : 1; + uint64_t MaximumMtu : 1; + uint64_t SendBufferingEnabled : 1; + uint64_t PacingEnabled : 1; + uint64_t MigrationEnabled : 1; + uint64_t DatagramReceiveEnabled : 1; + uint64_t ServerResumptionLevel : 1; + uint64_t MaxOperationsPerDrain : 1; + uint64_t MtuDiscoveryMissingProbeCount : 1; + uint64_t DestCidUpdateIdleTimeoutMs : 1; + uint64_t GreaseQuicBitEnabled : 1; + uint64_t EcnEnabled : 1; + uint64_t HyStartEnabled : 1; + uint64_t RESERVED : 29; + } IsSet; + }; + + uint64_t MaxBytesPerKey; + uint64_t HandshakeIdleTimeoutMs; + uint64_t IdleTimeoutMs; + uint64_t MtuDiscoverySearchCompleteTimeoutUs; + uint32_t TlsClientMaxSendBuffer; + uint32_t TlsServerMaxSendBuffer; + uint32_t StreamRecvWindowDefault; + uint32_t StreamRecvBufferDefault; + uint32_t ConnFlowControlWindow; + uint32_t MaxWorkerQueueDelayUs; + uint32_t MaxStatelessOperations; + uint32_t InitialWindowPackets; + uint32_t SendIdleTimeoutMs; + uint32_t InitialRttMs; + uint32_t MaxAckDelayMs; + uint32_t DisconnectTimeoutMs; + uint32_t KeepAliveIntervalMs; + uint16_t CongestionControlAlgorithm; // QUIC_CONGESTION_CONTROL_ALGORITHM + uint16_t PeerBidiStreamCount; + uint16_t PeerUnidiStreamCount; + uint16_t MaxBindingStatelessOperations; + uint16_t StatelessOperationExpirationMs; + uint16_t MinimumMtu; + uint16_t MaximumMtu; + uint8_t SendBufferingEnabled : 1; + uint8_t PacingEnabled : 1; + uint8_t MigrationEnabled : 1; + uint8_t DatagramReceiveEnabled : 1; + uint8_t ServerResumptionLevel : 2; // QUIC_SERVER_RESUMPTION_LEVEL + uint8_t GreaseQuicBitEnabled : 1; + uint8_t EcnEnabled : 1; + uint8_t MaxOperationsPerDrain; + uint8_t MtuDiscoveryMissingProbeCount; + uint32_t DestCidUpdateIdleTimeoutMs; + union { + uint64_t Flags; + struct { + uint64_t HyStartEnabled : 1; + uint64_t ReservedFlags : 63; + }; + }; + + +} QUIC_SETTINGS; + +// +// This struct enables QUIC applications to support SSLKEYLOGFILE +// for debugging packet captures with e.g. Wireshark. +// + +#define QUIC_TLS_SECRETS_MAX_SECRET_LEN 64 +typedef struct QUIC_TLS_SECRETS { + uint8_t SecretLength; + struct { + uint8_t ClientRandom : 1; + uint8_t ClientEarlyTrafficSecret : 1; + uint8_t ClientHandshakeTrafficSecret : 1; + uint8_t ServerHandshakeTrafficSecret : 1; + uint8_t ClientTrafficSecret0 : 1; + uint8_t ServerTrafficSecret0 : 1; + } IsSet; + uint8_t ClientRandom[32]; + uint8_t ClientEarlyTrafficSecret[QUIC_TLS_SECRETS_MAX_SECRET_LEN]; + uint8_t ClientHandshakeTrafficSecret[QUIC_TLS_SECRETS_MAX_SECRET_LEN]; + uint8_t ServerHandshakeTrafficSecret[QUIC_TLS_SECRETS_MAX_SECRET_LEN]; + uint8_t ClientTrafficSecret0[QUIC_TLS_SECRETS_MAX_SECRET_LEN]; + uint8_t ServerTrafficSecret0[QUIC_TLS_SECRETS_MAX_SECRET_LEN]; +} QUIC_TLS_SECRETS; + +typedef struct QUIC_STREAM_STATISTICS { + uint64_t ConnBlockedBySchedulingUs; + uint64_t ConnBlockedByPacingUs; + uint64_t ConnBlockedByAmplificationProtUs; + uint64_t ConnBlockedByCongestionControlUs; + uint64_t ConnBlockedByFlowControlUs; + uint64_t StreamBlockedByIdFlowControlUs; + uint64_t StreamBlockedByFlowControlUs; + uint64_t StreamBlockedByAppUs; +} QUIC_STREAM_STATISTICS; + +// +// Functions for associating application contexts with QUIC handles. MsQuic +// provides no explicit synchronization between parallel calls to these +// functions. +// + +typedef +_IRQL_requires_max_(DISPATCH_LEVEL) +void +(QUIC_API * QUIC_SET_CONTEXT_FN)( + _In_ _Pre_defensive_ HQUIC Handle, + _In_opt_ void* Context + ); + +typedef +_IRQL_requires_max_(DISPATCH_LEVEL) +void* +(QUIC_API * QUIC_GET_CONTEXT_FN)( + _In_ _Pre_defensive_ HQUIC Handle + ); + +// +// Sets the event handler for the QUIC handle. The type of the handler must be +// appropriate for the type of the handle. MsQuic provides no explicit +// synchronization between parallel calls to this function or the ones above. +// +typedef +_IRQL_requires_max_(DISPATCH_LEVEL) +void +(QUIC_API * QUIC_SET_CALLBACK_HANDLER_FN)( + _In_ _Pre_defensive_ HQUIC Handle, + _In_ void* Handler, + _In_opt_ void* Context + ); + +// +// Get and Set parameters on a handle. +// + +#define QUIC_PARAM_PREFIX_GLOBAL 0x01000000 +#define QUIC_PARAM_PREFIX_REGISTRATION 0x02000000 +#define QUIC_PARAM_PREFIX_CONFIGURATION 0x03000000 +#define QUIC_PARAM_PREFIX_LISTENER 0x04000000 +#define QUIC_PARAM_PREFIX_CONNECTION 0x05000000 +#define QUIC_PARAM_PREFIX_TLS 0x06000000 +#define QUIC_PARAM_PREFIX_TLS_SCHANNEL 0x07000000 +#define QUIC_PARAM_PREFIX_STREAM 0x08000000 + +#define QUIC_PARAM_IS_GLOBAL(Param) ((Param & 0x7F000000) == QUIC_PARAM_PREFIX_GLOBAL) + +// +// Parameters for Global. +// +#define QUIC_PARAM_GLOBAL_RETRY_MEMORY_PERCENT 0x01000000 // uint16_t +#define QUIC_PARAM_GLOBAL_SUPPORTED_VERSIONS 0x01000001 // uint32_t[] - network byte order +#define QUIC_PARAM_GLOBAL_LOAD_BALACING_MODE 0x01000002 // uint16_t - QUIC_LOAD_BALANCING_MODE +#define QUIC_PARAM_GLOBAL_PERF_COUNTERS 0x01000003 // uint64_t[] - Array size is QUIC_PERF_COUNTER_MAX +#define QUIC_PARAM_GLOBAL_LIBRARY_VERSION 0x01000004 // uint32_t[4] +#define QUIC_PARAM_GLOBAL_SETTINGS 0x01000005 // QUIC_SETTINGS +#define QUIC_PARAM_GLOBAL_GLOBAL_SETTINGS 0x01000006 // QUIC_GLOBAL_SETTINGS +#ifdef QUIC_API_ENABLE_PREVIEW_FEATURES +#define QUIC_PARAM_GLOBAL_VERSION_SETTINGS 0x01000007 // QUIC_VERSION_SETTINGS +#endif +#define QUIC_PARAM_GLOBAL_LIBRARY_GIT_HASH 0x01000008 // char[64] +#ifdef QUIC_API_ENABLE_PREVIEW_FEATURES +#define QUIC_PARAM_GLOBAL_EXECUTION_CONFIG 0x01000009 // QUIC_EXECUTION_CONFIG +#endif +#define QUIC_PARAM_GLOBAL_TLS_PROVIDER 0x0100000A // QUIC_TLS_PROVIDER + +// +// Parameters for Registration. +// + +// +// Parameters for Configuration. +// +#define QUIC_PARAM_CONFIGURATION_SETTINGS 0x03000000 // QUIC_SETTINGS +#define QUIC_PARAM_CONFIGURATION_TICKET_KEYS 0x03000001 // QUIC_TICKET_KEY_CONFIG[] +#ifdef QUIC_API_ENABLE_PREVIEW_FEATURES +#define QUIC_PARAM_CONFIGURATION_VERSION_SETTINGS 0x03000002 // QUIC_VERSION_SETTINGS +#endif +// Schannel-specific Configuration parameter +typedef struct QUIC_SCHANNEL_CREDENTIAL_ATTRIBUTE_W { + unsigned long Attribute; + unsigned long BufferLength; + void* Buffer; +} QUIC_SCHANNEL_CREDENTIAL_ATTRIBUTE_W; +#define QUIC_PARAM_CONFIGURATION_SCHANNEL_CREDENTIAL_ATTRIBUTE_W 0x03000003 // QUIC_SCHANNEL_CREDENTIAL_ATTRIBUTE_W + +// +// Parameters for Listener. +// +#define QUIC_PARAM_LISTENER_LOCAL_ADDRESS 0x04000000 // QUIC_ADDR +#define QUIC_PARAM_LISTENER_STATS 0x04000001 // QUIC_LISTENER_STATISTICS +#ifdef QUIC_API_ENABLE_PREVIEW_FEATURES +#define QUIC_PARAM_LISTENER_CIBIR_ID 0x04000002 // uint8_t[] {offset, id[]} +#endif + +// +// Parameters for Connection. +// +#define QUIC_PARAM_CONN_QUIC_VERSION 0x05000000 // uint32_t +#define QUIC_PARAM_CONN_LOCAL_ADDRESS 0x05000001 // QUIC_ADDR +#define QUIC_PARAM_CONN_REMOTE_ADDRESS 0x05000002 // QUIC_ADDR +#define QUIC_PARAM_CONN_IDEAL_PROCESSOR 0x05000003 // uint16_t +#define QUIC_PARAM_CONN_SETTINGS 0x05000004 // QUIC_SETTINGS +#define QUIC_PARAM_CONN_STATISTICS 0x05000005 // QUIC_STATISTICS +#define QUIC_PARAM_CONN_STATISTICS_PLAT 0x05000006 // QUIC_STATISTICS +#define QUIC_PARAM_CONN_SHARE_UDP_BINDING 0x05000007 // uint8_t (BOOLEAN) +#define QUIC_PARAM_CONN_LOCAL_BIDI_STREAM_COUNT 0x05000008 // uint16_t +#define QUIC_PARAM_CONN_LOCAL_UNIDI_STREAM_COUNT 0x05000009 // uint16_t +#define QUIC_PARAM_CONN_MAX_STREAM_IDS 0x0500000A // uint64_t[4] +#define QUIC_PARAM_CONN_CLOSE_REASON_PHRASE 0x0500000B // char[] +#define QUIC_PARAM_CONN_STREAM_SCHEDULING_SCHEME 0x0500000C // QUIC_STREAM_SCHEDULING_SCHEME +#define QUIC_PARAM_CONN_DATAGRAM_RECEIVE_ENABLED 0x0500000D // uint8_t (BOOLEAN) +#define QUIC_PARAM_CONN_DATAGRAM_SEND_ENABLED 0x0500000E // uint8_t (BOOLEAN) +#ifdef QUIC_API_ENABLE_INSECURE_FEATURES +#define QUIC_PARAM_CONN_DISABLE_1RTT_ENCRYPTION 0x0500000F // uint8_t (BOOLEAN) +#endif +#define QUIC_PARAM_CONN_RESUMPTION_TICKET 0x05000010 // uint8_t[] +#define QUIC_PARAM_CONN_PEER_CERTIFICATE_VALID 0x05000011 // uint8_t (BOOLEAN) +#define QUIC_PARAM_CONN_LOCAL_INTERFACE 0x05000012 // uint32_t +#define QUIC_PARAM_CONN_TLS_SECRETS 0x05000013 // QUIC_TLS_SECRETS (SSLKEYLOGFILE compatible) +#ifdef QUIC_API_ENABLE_PREVIEW_FEATURES +#define QUIC_PARAM_CONN_VERSION_SETTINGS 0x05000014 // QUIC_VERSION_SETTINGS +#define QUIC_PARAM_CONN_CIBIR_ID 0x05000015 // uint8_t[] {offset, id[]} +#endif +#define QUIC_PARAM_CONN_STATISTICS_V2 0x05000016 // QUIC_STATISTICS_V2 +#define QUIC_PARAM_CONN_STATISTICS_V2_PLAT 0x05000017 // QUIC_STATISTICS_V2 + +// +// Parameters for TLS. +// +#define QUIC_PARAM_TLS_HANDSHAKE_INFO 0x06000000 // QUIC_HANDSHAKE_INFO +#define QUIC_PARAM_TLS_NEGOTIATED_ALPN 0x06000001 // uint8_t[] (max 255 bytes) + +#ifdef WIN32 // Schannel specific TLS parameters +typedef struct QUIC_SCHANNEL_CONTEXT_ATTRIBUTE_W { + unsigned long Attribute; + void* Buffer; +} QUIC_SCHANNEL_CONTEXT_ATTRIBUTE_W; +#define QUIC_PARAM_TLS_SCHANNEL_CONTEXT_ATTRIBUTE_W 0x07000000 // QUIC_SCHANNEL_CONTEXT_ATTRIBUTE_W +typedef struct QUIC_SCHANNEL_CONTEXT_ATTRIBUTE_EX_W { + unsigned long Attribute; + unsigned long BufferLength; + void* Buffer; +} QUIC_SCHANNEL_CONTEXT_ATTRIBUTE_EX_W; +#define QUIC_PARAM_TLS_SCHANNEL_CONTEXT_ATTRIBUTE_EX_W 0x07000001 // QUIC_SCHANNEL_CONTEXT_ATTRIBUTE_EX_W +#define QUIC_PARAM_TLS_SCHANNEL_SECURITY_CONTEXT_TOKEN 0x07000002 // HANDLE +#endif + +// +// Parameters for Stream. +// +#define QUIC_PARAM_STREAM_ID 0x08000000 // QUIC_UINT62 +#define QUIC_PARAM_STREAM_0RTT_LENGTH 0x08000001 // uint64_t +#define QUIC_PARAM_STREAM_IDEAL_SEND_BUFFER_SIZE 0x08000002 // uint64_t - bytes +#define QUIC_PARAM_STREAM_PRIORITY 0x08000003 // uint16_t - 0 (low) to 0xFFFF (high) - 0x7FFF (default) +#define QUIC_PARAM_STREAM_STATISTICS 0X08000004 // QUIC_STREAM_STATISTICS + +typedef +_IRQL_requires_max_(PASSIVE_LEVEL) +QUIC_STATUS +(QUIC_API * QUIC_SET_PARAM_FN)( + _When_(QUIC_PARAM_IS_GLOBAL(Param), _Reserved_) + _When_(!QUIC_PARAM_IS_GLOBAL(Param), _In_ _Pre_defensive_) + HQUIC Handle, + _In_ uint32_t Param, + _In_ uint32_t BufferLength, + _In_reads_bytes_(BufferLength) + const void* Buffer + ); + +typedef +_IRQL_requires_max_(PASSIVE_LEVEL) +QUIC_STATUS +(QUIC_API * QUIC_GET_PARAM_FN)( + _When_(QUIC_PARAM_IS_GLOBAL(Param), _Reserved_) + _When_(!QUIC_PARAM_IS_GLOBAL(Param), _In_ _Pre_defensive_) + HQUIC Handle, + _In_ uint32_t Param, + _Inout_ _Pre_defensive_ uint32_t* BufferLength, + _Out_writes_bytes_opt_(*BufferLength) + void* Buffer + ); + +// +// Registration Context Interface. +// + +// +// Opens a new registration. +// +typedef +_IRQL_requires_max_(PASSIVE_LEVEL) +QUIC_STATUS +(QUIC_API * QUIC_REGISTRATION_OPEN_FN)( + _In_opt_ const QUIC_REGISTRATION_CONFIG* Config, + _Outptr_ _At_(*Registration, __drv_allocatesMem(Mem)) _Pre_defensive_ + HQUIC* Registration + ); + +// +// Closes the registration. This function synchronizes the cleanup of all +// child objects. It does this by blocking until all those child objects have +// been closed by the application. +// N.B. This function will deadlock if called in any MsQuic callbacks. +// +typedef +_IRQL_requires_max_(PASSIVE_LEVEL) +void +(QUIC_API * QUIC_REGISTRATION_CLOSE_FN)( + _In_ _Pre_defensive_ __drv_freesMem(Mem) + HQUIC Registration + ); + +// +// Calls shutdown for all connections in this registration. Don't call on a +// MsQuic callback thread or it might deadlock. +// +typedef +_IRQL_requires_max_(DISPATCH_LEVEL) +void +(QUIC_API * QUIC_REGISTRATION_SHUTDOWN_FN)( + _In_ _Pre_defensive_ HQUIC Registration, + _In_ QUIC_CONNECTION_SHUTDOWN_FLAGS Flags, + _In_ _Pre_defensive_ QUIC_UINT62 ErrorCode // Application defined error code + ); + +// +// Configuration Interface. +// + +// +// Opens a new configuration. +// +typedef +_IRQL_requires_max_(PASSIVE_LEVEL) +QUIC_STATUS +(QUIC_API * QUIC_CONFIGURATION_OPEN_FN)( + _In_ _Pre_defensive_ HQUIC Registration, + _In_reads_(AlpnBufferCount) _Pre_defensive_ + const QUIC_BUFFER* const AlpnBuffers, + _In_range_(>, 0) uint32_t AlpnBufferCount, + _In_reads_bytes_opt_(SettingsSize) + const QUIC_SETTINGS* Settings, + _In_ uint32_t SettingsSize, + _In_opt_ void* Context, + _Outptr_ _At_(*Configuration, __drv_allocatesMem(Mem)) _Pre_defensive_ + HQUIC* Configuration + ); + +// +// Closes an existing configuration. +// +typedef +_IRQL_requires_max_(PASSIVE_LEVEL) +void +(QUIC_API * QUIC_CONFIGURATION_CLOSE_FN)( + _In_ _Pre_defensive_ __drv_freesMem(Mem) + HQUIC Configuration + ); + +// +// Loads the credentials based on the input configuration. +// +typedef +_IRQL_requires_max_(PASSIVE_LEVEL) +QUIC_STATUS +(QUIC_API * QUIC_CONFIGURATION_LOAD_CREDENTIAL_FN)( + _In_ _Pre_defensive_ HQUIC Configuration, + _In_ _Pre_defensive_ const QUIC_CREDENTIAL_CONFIG* CredConfig + ); + +// +// Listener Context Interface. +// + +typedef enum QUIC_LISTENER_EVENT_TYPE { + QUIC_LISTENER_EVENT_NEW_CONNECTION = 0, + QUIC_LISTENER_EVENT_STOP_COMPLETE = 1, +} QUIC_LISTENER_EVENT_TYPE; + +typedef struct QUIC_LISTENER_EVENT { + QUIC_LISTENER_EVENT_TYPE Type; + union { + struct { + const QUIC_NEW_CONNECTION_INFO* Info; + HQUIC Connection; + } NEW_CONNECTION; + struct { + BOOLEAN AppCloseInProgress : 1; + BOOLEAN RESERVED : 7; + } STOP_COMPLETE; + }; +} QUIC_LISTENER_EVENT; + +typedef +_IRQL_requires_max_(PASSIVE_LEVEL) +_Function_class_(QUIC_LISTENER_CALLBACK) +QUIC_STATUS +(QUIC_API QUIC_LISTENER_CALLBACK)( + _In_ HQUIC Listener, + _In_opt_ void* Context, + _Inout_ QUIC_LISTENER_EVENT* Event + ); + +typedef QUIC_LISTENER_CALLBACK *QUIC_LISTENER_CALLBACK_HANDLER; + +// +// Opens a new listener. +// +typedef +_IRQL_requires_max_(PASSIVE_LEVEL) +QUIC_STATUS +(QUIC_API * QUIC_LISTENER_OPEN_FN)( + _In_ _Pre_defensive_ HQUIC Registration, + _In_ _Pre_defensive_ QUIC_LISTENER_CALLBACK_HANDLER Handler, + _In_opt_ void* Context, + _Outptr_ _At_(*Listener, __drv_allocatesMem(Mem)) _Pre_defensive_ + HQUIC* Listener + ); + +// +// Closes an existing listener. +// +typedef +_IRQL_requires_max_(PASSIVE_LEVEL) +void +(QUIC_API * QUIC_LISTENER_CLOSE_FN)( + _In_ _Pre_defensive_ __drv_freesMem(Mem) + HQUIC Listener + ); + +// +// Starts the listener processing incoming connections. +// +typedef +_IRQL_requires_max_(PASSIVE_LEVEL) +QUIC_STATUS +(QUIC_API * QUIC_LISTENER_START_FN)( + _In_ _Pre_defensive_ HQUIC Listener, + _In_reads_(AlpnBufferCount) _Pre_defensive_ + const QUIC_BUFFER* const AlpnBuffers, + _In_range_(>, 0) uint32_t AlpnBufferCount, + _In_opt_ const QUIC_ADDR* LocalAddress + ); + +// +// Asynchronously stops the listener from processing incoming connections. +// +typedef +_IRQL_requires_max_(PASSIVE_LEVEL) +void +(QUIC_API * QUIC_LISTENER_STOP_FN)( + _In_ _Pre_defensive_ HQUIC Listener + ); + +// +// Connections +// + +typedef enum QUIC_CONNECTION_EVENT_TYPE { + QUIC_CONNECTION_EVENT_CONNECTED = 0, + QUIC_CONNECTION_EVENT_SHUTDOWN_INITIATED_BY_TRANSPORT = 1, // The transport started the shutdown process. + QUIC_CONNECTION_EVENT_SHUTDOWN_INITIATED_BY_PEER = 2, // The peer application started the shutdown process. + QUIC_CONNECTION_EVENT_SHUTDOWN_COMPLETE = 3, // Ready for the handle to be closed. + QUIC_CONNECTION_EVENT_LOCAL_ADDRESS_CHANGED = 4, + QUIC_CONNECTION_EVENT_PEER_ADDRESS_CHANGED = 5, + QUIC_CONNECTION_EVENT_PEER_STREAM_STARTED = 6, + QUIC_CONNECTION_EVENT_STREAMS_AVAILABLE = 7, + QUIC_CONNECTION_EVENT_PEER_NEEDS_STREAMS = 8, + QUIC_CONNECTION_EVENT_IDEAL_PROCESSOR_CHANGED = 9, + QUIC_CONNECTION_EVENT_DATAGRAM_STATE_CHANGED = 10, + QUIC_CONNECTION_EVENT_DATAGRAM_RECEIVED = 11, + QUIC_CONNECTION_EVENT_DATAGRAM_SEND_STATE_CHANGED = 12, + QUIC_CONNECTION_EVENT_RESUMED = 13, // Server-only; provides resumption data, if any. + QUIC_CONNECTION_EVENT_RESUMPTION_TICKET_RECEIVED = 14, // Client-only; provides ticket to persist, if any. + QUIC_CONNECTION_EVENT_PEER_CERTIFICATE_RECEIVED = 15, // Only with QUIC_CREDENTIAL_FLAG_INDICATE_CERTIFICATE_RECEIVED set +} QUIC_CONNECTION_EVENT_TYPE; + +typedef struct QUIC_CONNECTION_EVENT { + QUIC_CONNECTION_EVENT_TYPE Type; + union { + struct { + BOOLEAN SessionResumed; + _Field_range_(>, 0) + uint8_t NegotiatedAlpnLength; + _Field_size_(NegotiatedAlpnLength) + const uint8_t* NegotiatedAlpn; + } CONNECTED; + struct { + QUIC_STATUS Status; + QUIC_UINT62 ErrorCode; // Wire format error code. + } SHUTDOWN_INITIATED_BY_TRANSPORT; + struct { + QUIC_UINT62 ErrorCode; + } SHUTDOWN_INITIATED_BY_PEER; + struct { + BOOLEAN HandshakeCompleted : 1; + BOOLEAN PeerAcknowledgedShutdown : 1; + BOOLEAN AppCloseInProgress : 1; + } SHUTDOWN_COMPLETE; + struct { + const QUIC_ADDR* Address; + } LOCAL_ADDRESS_CHANGED; + struct { + const QUIC_ADDR* Address; + } PEER_ADDRESS_CHANGED; + struct { + HQUIC Stream; + QUIC_STREAM_OPEN_FLAGS Flags; + } PEER_STREAM_STARTED; + struct { + uint16_t BidirectionalCount; + uint16_t UnidirectionalCount; + } STREAMS_AVAILABLE; + struct { + BOOLEAN Bidirectional; + } PEER_NEEDS_STREAMS; + struct { + uint16_t IdealProcessor; + } IDEAL_PROCESSOR_CHANGED; + struct { + BOOLEAN SendEnabled; + uint16_t MaxSendLength; + } DATAGRAM_STATE_CHANGED; + struct { + const QUIC_BUFFER* Buffer; + QUIC_RECEIVE_FLAGS Flags; + } DATAGRAM_RECEIVED; + struct { + /* inout */ void* ClientContext; + QUIC_DATAGRAM_SEND_STATE State; + } DATAGRAM_SEND_STATE_CHANGED; + struct { + uint16_t ResumptionStateLength; + const uint8_t* ResumptionState; + } RESUMED; + struct { + _Field_range_(>, 0) + uint32_t ResumptionTicketLength; + _Field_size_(ResumptionTicketLength) + const uint8_t* ResumptionTicket; + } RESUMPTION_TICKET_RECEIVED; + struct { + QUIC_CERTIFICATE* Certificate; // Peer certificate (platform specific). Valid only during QUIC_CONNECTION_EVENT_PEER_CERTIFICATE_RECEIVED callback. + uint32_t DeferredErrorFlags; // Bit flag of errors (only valid with QUIC_CREDENTIAL_FLAG_DEFER_CERTIFICATE_VALIDATION) - Schannel only, zero otherwise. + QUIC_STATUS DeferredStatus; // Most severe error status (only valid with QUIC_CREDENTIAL_FLAG_DEFER_CERTIFICATE_VALIDATION) + QUIC_CERTIFICATE_CHAIN* Chain; // Peer certificate chain (platform specific). Valid only during QUIC_CONNECTION_EVENT_PEER_CERTIFICATE_RECEIVED callback. + } PEER_CERTIFICATE_RECEIVED; + }; +} QUIC_CONNECTION_EVENT; + +typedef +_IRQL_requires_max_(PASSIVE_LEVEL) +_Function_class_(QUIC_CONNECTION_CALLBACK) +QUIC_STATUS +(QUIC_API QUIC_CONNECTION_CALLBACK)( + _In_ HQUIC Connection, + _In_opt_ void* Context, + _Inout_ QUIC_CONNECTION_EVENT* Event + ); + +typedef QUIC_CONNECTION_CALLBACK *QUIC_CONNECTION_CALLBACK_HANDLER; + +// +// Opens a new connection. +// +typedef +_IRQL_requires_max_(DISPATCH_LEVEL) +QUIC_STATUS +(QUIC_API * QUIC_CONNECTION_OPEN_FN)( + _In_ _Pre_defensive_ HQUIC Registration, + _In_ _Pre_defensive_ QUIC_CONNECTION_CALLBACK_HANDLER Handler, + _In_opt_ void* Context, + _Outptr_ _At_(*Connection, __drv_allocatesMem(Mem)) _Pre_defensive_ + HQUIC* Connection + ); + +// +// Closes an existing connection. +// +typedef +_IRQL_requires_max_(PASSIVE_LEVEL) +void +(QUIC_API * QUIC_CONNECTION_CLOSE_FN)( + _In_ _Pre_defensive_ __drv_freesMem(Mem) + HQUIC Connection + ); + +// +// Starts the shutdown process on the connection. This immediately and silently +// shuts down any open streams; which will trigger callbacks for +// QUIC_CONNECTION_EVENT_STREAM_CLOSED events. Does nothing if already shutdown. +// Can be passed either a connection or stream handle. +// +typedef +_IRQL_requires_max_(DISPATCH_LEVEL) +void +(QUIC_API * QUIC_CONNECTION_SHUTDOWN_FN)( + _In_ _Pre_defensive_ HQUIC Connection, + _In_ QUIC_CONNECTION_SHUTDOWN_FLAGS Flags, + _In_ _Pre_defensive_ QUIC_UINT62 ErrorCode // Application defined error code + ); + +// +// Uses the QUIC (client) handle to start a connection attempt to the +// remote server. Can be passed either a connection or stream handle. +// +typedef +_IRQL_requires_max_(DISPATCH_LEVEL) +QUIC_STATUS +(QUIC_API * QUIC_CONNECTION_START_FN)( + _In_ _Pre_defensive_ HQUIC Connection, + _In_ _Pre_defensive_ HQUIC Configuration, + _In_ QUIC_ADDRESS_FAMILY Family, + _In_reads_or_z_opt_(QUIC_MAX_SNI_LENGTH) + const char* ServerName, + _In_ uint16_t ServerPort // Host byte order + ); + +// +// Sets the (server-side) configuration handle for the connection. This must be +// called on an accepted connection in order to proceed with the QUIC handshake. +// +typedef +_IRQL_requires_max_(DISPATCH_LEVEL) +QUIC_STATUS +(QUIC_API * QUIC_CONNECTION_SET_CONFIGURATION_FN)( + _In_ _Pre_defensive_ HQUIC Connection, + _In_ _Pre_defensive_ HQUIC Configuration + ); + +// +// Uses the QUIC (server) handle to send a resumption ticket to the remote +// client, optionally with app-specific data useful during resumption. +// +typedef +_IRQL_requires_max_(DISPATCH_LEVEL) +QUIC_STATUS +(QUIC_API * QUIC_CONNECTION_SEND_RESUMPTION_FN)( + _In_ _Pre_defensive_ HQUIC Connection, + _In_ QUIC_SEND_RESUMPTION_FLAGS Flags, + _In_ uint16_t DataLength, + _In_reads_bytes_opt_(DataLength) + const uint8_t* ResumptionData + ); + +// +// Uses the QUIC (server) handle to complete resumption ticket validation. +// This must be called after server app handles ticket validation and then +// return QUIC_STATUS_PENDING. +// +typedef +_IRQL_requires_max_(DISPATCH_LEVEL) +QUIC_STATUS +(QUIC_API * QUIC_CONNECTION_COMP_RESUMPTION_FN)( + _In_ _Pre_defensive_ HQUIC Connection, + _In_ BOOLEAN Result + ); + +// +// Uses the QUIC (client) handle to complete certificate validation. +// This must be called after client app handles certificate validation +// and then return QUIC_STATUS_PENDING. The TlsAlert value is ignored if Result +// equals TRUE (recommend just pass QUIC_TLS_ALERT_CODE_SUCCESS). +// +typedef +_IRQL_requires_max_(DISPATCH_LEVEL) +QUIC_STATUS +(QUIC_API * QUIC_CONNECTION_COMP_CERT_FN)( + _In_ _Pre_defensive_ HQUIC Connection, + _In_ BOOLEAN Result, + _In_ QUIC_TLS_ALERT_CODES TlsAlert + ); + +// +// Streams +// + +typedef enum QUIC_STREAM_EVENT_TYPE { + QUIC_STREAM_EVENT_START_COMPLETE = 0, + QUIC_STREAM_EVENT_RECEIVE = 1, + QUIC_STREAM_EVENT_SEND_COMPLETE = 2, + QUIC_STREAM_EVENT_PEER_SEND_SHUTDOWN = 3, + QUIC_STREAM_EVENT_PEER_SEND_ABORTED = 4, + QUIC_STREAM_EVENT_PEER_RECEIVE_ABORTED = 5, + QUIC_STREAM_EVENT_SEND_SHUTDOWN_COMPLETE = 6, + QUIC_STREAM_EVENT_SHUTDOWN_COMPLETE = 7, + QUIC_STREAM_EVENT_IDEAL_SEND_BUFFER_SIZE = 8, + QUIC_STREAM_EVENT_PEER_ACCEPTED = 9, +} QUIC_STREAM_EVENT_TYPE; + +typedef struct QUIC_STREAM_EVENT { + QUIC_STREAM_EVENT_TYPE Type; + union { + struct { + QUIC_STATUS Status; + QUIC_UINT62 ID; + BOOLEAN PeerAccepted : 1; + BOOLEAN RESERVED : 7; + } START_COMPLETE; + struct { + /* in */ uint64_t AbsoluteOffset; + /* inout */ uint64_t TotalBufferLength; + _Field_size_(BufferCount) + /* in */ const QUIC_BUFFER* Buffers; + _Field_range_(0, UINT32_MAX) + /* in */ uint32_t BufferCount; + /* in */ QUIC_RECEIVE_FLAGS Flags; + } RECEIVE; + struct { + BOOLEAN Canceled; + void* ClientContext; + } SEND_COMPLETE; + struct { + QUIC_UINT62 ErrorCode; + } PEER_SEND_ABORTED; + struct { + QUIC_UINT62 ErrorCode; + } PEER_RECEIVE_ABORTED; + struct { + BOOLEAN Graceful; + } SEND_SHUTDOWN_COMPLETE; + struct { + BOOLEAN ConnectionShutdown; + BOOLEAN AppCloseInProgress : 1; + BOOLEAN ConnectionShutdownByApp : 1; + BOOLEAN ConnectionClosedRemotely : 1; + BOOLEAN RESERVED : 5; + QUIC_UINT62 ConnectionErrorCode; + QUIC_STATUS ConnectionCloseStatus; + } SHUTDOWN_COMPLETE; + struct { + uint64_t ByteCount; + } IDEAL_SEND_BUFFER_SIZE; + }; +} QUIC_STREAM_EVENT; + +typedef +_IRQL_requires_max_(PASSIVE_LEVEL) +_Function_class_(QUIC_STREAM_CALLBACK) +QUIC_STATUS +(QUIC_API QUIC_STREAM_CALLBACK)( + _In_ HQUIC Stream, + _In_opt_ void* Context, + _Inout_ QUIC_STREAM_EVENT* Event + ); + +typedef QUIC_STREAM_CALLBACK *QUIC_STREAM_CALLBACK_HANDLER; + +// +// Opens a stream on the given connection. +// +typedef +_IRQL_requires_max_(DISPATCH_LEVEL) +QUIC_STATUS +(QUIC_API * QUIC_STREAM_OPEN_FN)( + _In_ _Pre_defensive_ HQUIC Connection, + _In_ QUIC_STREAM_OPEN_FLAGS Flags, + _In_ _Pre_defensive_ QUIC_STREAM_CALLBACK_HANDLER Handler, + _In_opt_ void* Context, + _Outptr_ _At_(*Stream, __drv_allocatesMem(Mem)) _Pre_defensive_ + HQUIC* Stream + ); + +// +// Closes a stream handle. +// +typedef +_IRQL_requires_max_(PASSIVE_LEVEL) +void +(QUIC_API * QUIC_STREAM_CLOSE_FN)( + _In_ _Pre_defensive_ __drv_freesMem(Mem) + HQUIC Stream + ); + +// +// Starts processing the stream. +// +typedef +_IRQL_requires_max_(DISPATCH_LEVEL) +QUIC_STATUS +(QUIC_API * QUIC_STREAM_START_FN)( + _In_ _Pre_defensive_ HQUIC Stream, + _In_ QUIC_STREAM_START_FLAGS Flags + ); + +// +// Shuts the stream down as specified, and waits for graceful +// shutdowns to complete. Does nothing if already shut down. +// +typedef +_IRQL_requires_max_(DISPATCH_LEVEL) +QUIC_STATUS +(QUIC_API * QUIC_STREAM_SHUTDOWN_FN)( + _In_ _Pre_defensive_ HQUIC Stream, + _In_ QUIC_STREAM_SHUTDOWN_FLAGS Flags, + _In_ _Pre_defensive_ QUIC_UINT62 ErrorCode // Application defined error code + ); + +// +// Sends data on an open stream. +// +typedef +_IRQL_requires_max_(DISPATCH_LEVEL) +QUIC_STATUS +(QUIC_API * QUIC_STREAM_SEND_FN)( + _In_ _Pre_defensive_ HQUIC Stream, + _In_reads_(BufferCount) _Pre_defensive_ + const QUIC_BUFFER* const Buffers, + _In_ uint32_t BufferCount, + _In_ QUIC_SEND_FLAGS Flags, + _In_opt_ void* ClientSendContext + ); + +// +// Completes a previously pended receive callback. +// +typedef +_IRQL_requires_max_(DISPATCH_LEVEL) +void +(QUIC_API * QUIC_STREAM_RECEIVE_COMPLETE_FN)( + _In_ _Pre_defensive_ HQUIC Stream, + _In_ uint64_t BufferLength + ); + +// +// Enables or disables stream receive callbacks. +// +typedef +_IRQL_requires_max_(DISPATCH_LEVEL) +QUIC_STATUS +(QUIC_API * QUIC_STREAM_RECEIVE_SET_ENABLED_FN)( + _In_ _Pre_defensive_ HQUIC Stream, + _In_ BOOLEAN IsEnabled + ); + +// +// Datagrams +// + +// +// Sends an unreliable datagram on the connection. Note, the total payload +// of the send must fit in a single QUIC packet. +// +typedef +_IRQL_requires_max_(DISPATCH_LEVEL) +QUIC_STATUS +(QUIC_API * QUIC_DATAGRAM_SEND_FN)( + _In_ _Pre_defensive_ HQUIC Connection, + _In_reads_(BufferCount) _Pre_defensive_ + const QUIC_BUFFER* const Buffers, + _In_ uint32_t BufferCount, + _In_ QUIC_SEND_FLAGS Flags, + _In_opt_ void* ClientSendContext + ); + +// +// Version 2 API Function Table. Returned from MsQuicOpenVersion when Version +// is 2. Also returned from MsQuicOpen2. +// +typedef struct QUIC_API_TABLE { + + QUIC_SET_CONTEXT_FN SetContext; + QUIC_GET_CONTEXT_FN GetContext; + QUIC_SET_CALLBACK_HANDLER_FN SetCallbackHandler; + + QUIC_SET_PARAM_FN SetParam; + QUIC_GET_PARAM_FN GetParam; + + QUIC_REGISTRATION_OPEN_FN RegistrationOpen; + QUIC_REGISTRATION_CLOSE_FN RegistrationClose; + QUIC_REGISTRATION_SHUTDOWN_FN RegistrationShutdown; + + QUIC_CONFIGURATION_OPEN_FN ConfigurationOpen; + QUIC_CONFIGURATION_CLOSE_FN ConfigurationClose; + QUIC_CONFIGURATION_LOAD_CREDENTIAL_FN + ConfigurationLoadCredential; + + QUIC_LISTENER_OPEN_FN ListenerOpen; + QUIC_LISTENER_CLOSE_FN ListenerClose; + QUIC_LISTENER_START_FN ListenerStart; + QUIC_LISTENER_STOP_FN ListenerStop; + + QUIC_CONNECTION_OPEN_FN ConnectionOpen; + QUIC_CONNECTION_CLOSE_FN ConnectionClose; + QUIC_CONNECTION_SHUTDOWN_FN ConnectionShutdown; + QUIC_CONNECTION_START_FN ConnectionStart; + QUIC_CONNECTION_SET_CONFIGURATION_FN + ConnectionSetConfiguration; + QUIC_CONNECTION_SEND_RESUMPTION_FN ConnectionSendResumptionTicket; + + QUIC_STREAM_OPEN_FN StreamOpen; + QUIC_STREAM_CLOSE_FN StreamClose; + QUIC_STREAM_START_FN StreamStart; + QUIC_STREAM_SHUTDOWN_FN StreamShutdown; + QUIC_STREAM_SEND_FN StreamSend; + QUIC_STREAM_RECEIVE_COMPLETE_FN StreamReceiveComplete; + QUIC_STREAM_RECEIVE_SET_ENABLED_FN StreamReceiveSetEnabled; + + QUIC_DATAGRAM_SEND_FN DatagramSend; + + QUIC_CONNECTION_COMP_RESUMPTION_FN ConnectionResumptionTicketValidationComplete; // Available from v2.2 + QUIC_CONNECTION_COMP_CERT_FN ConnectionCertificateValidationComplete; // Available from v2.2 + +} QUIC_API_TABLE; + +#define QUIC_API_VERSION_1 1 // Not supported any more +#define QUIC_API_VERSION_2 2 // Current latest + +#if defined(_KERNEL_MODE) && !defined(_WIN64) + +// +// 32 bit kernel mode is no longer supported, so shim behavior in 32 bit kernel +// mode +// +#define MsQuicClose(QuicApi) UNREFERENCED_PARAMETER((QuicApi)) +#define MsQuicOpenVersion(Version, QuicApi) QUIC_STATUS_NOT_SUPPORTED + +#else + +// +// Opens the API library and initializes it if this is the first call for the +// process. It returns API function table for the rest of the API's functions. +// MsQuicClose must be called when the app is done with the function table. +// +_IRQL_requires_max_(PASSIVE_LEVEL) +_Check_return_ +#if (__cplusplus >= 201703L || _MSVC_LANG >= 201703L) +[[nodiscard]] +#endif +QUIC_STATUS +QUIC_API +MsQuicOpenVersion( + _In_ uint32_t Version, + _Out_ _Pre_defensive_ const void** QuicApi + ); + +// +// Cleans up the function table returned from MsQuicOpenVersion and releases the +// reference on the API. +// +_IRQL_requires_max_(PASSIVE_LEVEL) +void +QUIC_API +MsQuicClose( + _In_ _Pre_defensive_ const void* QuicApi + ); + +#endif + +// +// Version specific helpers that wrap MsQuicOpenVersion. +// + +#if defined(__cplusplus) + +_IRQL_requires_max_(PASSIVE_LEVEL) +_Check_return_ +#if (__cplusplus >= 201703L || _MSVC_LANG >= 201703L) +[[nodiscard]] +#endif +#ifdef WIN32 +__forceinline +#else +__attribute__((always_inline)) inline +#endif +QUIC_STATUS +MsQuicOpen2( + _Out_ _Pre_defensive_ const QUIC_API_TABLE** QuicApi + ) +{ + return MsQuicOpenVersion(QUIC_API_VERSION_2, (const void**)QuicApi); +} + +#else + +#define MsQuicOpen2(QuicApi) MsQuicOpenVersion(2, (const void**)QuicApi) + +#endif // defined(__cplusplus) + +#if defined(__cplusplus) +} +#endif + +#endif // _MSQUIC_ diff --git a/msquic/include/msquic_winuser.h b/msquic/include/msquic_winuser.h new file mode 100644 index 0000000..adc2f22 --- /dev/null +++ b/msquic/include/msquic_winuser.h @@ -0,0 +1,376 @@ +/*++ + + Copyright (c) Microsoft Corporation. + Licensed under the MIT License. + +Abstract: + + This file contains the platform specific definitions for MsQuic structures + and error codes. + +Environment: + + Windows User mode + +--*/ + +#pragma once + +#ifndef _MSQUIC_WINUSER_ +#define _MSQUIC_WINUSER_ + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif + +#pragma warning(push) +#pragma warning(disable:6553) // Annotation does not apply to value type. +#include +#pragma warning(pop) +#include +#include +#pragma warning(push) +#pragma warning(disable:6385) // Invalid data: accessing [buffer-name], the readable size is size1 bytes but size2 bytes may be read +#pragma warning(disable:6101) // Returning uninitialized memory +#include +#include +#pragma warning(pop) + +#include + +#define SUCCESS_HRESULT_FROM_WIN32(x) \ + ((HRESULT)(((x) & 0x0000FFFF) | (FACILITY_WIN32 << 16))) + +#ifndef ERROR_QUIC_HANDSHAKE_FAILURE +#define ERROR_QUIC_HANDSHAKE_FAILURE _HRESULT_TYPEDEF_(0x80410000L) +#endif + +#ifndef ERROR_QUIC_VER_NEG_FAILURE +#define ERROR_QUIC_VER_NEG_FAILURE _HRESULT_TYPEDEF_(0x80410001L) +#endif + +#ifndef ERROR_QUIC_USER_CANCELED +#define ERROR_QUIC_USER_CANCELED _HRESULT_TYPEDEF_(0x80410002L) +#endif + +#ifndef ERROR_QUIC_INTERNAL_ERROR +#define ERROR_QUIC_INTERNAL_ERROR _HRESULT_TYPEDEF_(0x80410003L) +#endif + +#ifndef ERROR_QUIC_PROTOCOL_VIOLATION +#define ERROR_QUIC_PROTOCOL_VIOLATION _HRESULT_TYPEDEF_(0x80410004L) +#endif + +#ifndef ERROR_QUIC_CONNECTION_IDLE +#define ERROR_QUIC_CONNECTION_IDLE _HRESULT_TYPEDEF_(0x80410005L) +#endif + +#ifndef ERROR_QUIC_CONNECTION_TIMEOUT +#define ERROR_QUIC_CONNECTION_TIMEOUT _HRESULT_TYPEDEF_(0x80410006L) +#endif + +#ifndef ERROR_QUIC_ALPN_NEG_FAILURE +#define ERROR_QUIC_ALPN_NEG_FAILURE _HRESULT_TYPEDEF_(0x80410007L) +#endif + +#ifndef ERROR_QUIC_STREAM_LIMIT_REACHED +#define ERROR_QUIC_STREAM_LIMIT_REACHED _HRESULT_TYPEDEF_(0x80410008L) +#endif + +#ifndef ERROR_QUIC_ALPN_IN_USE +#define ERROR_QUIC_ALPN_IN_USE _HRESULT_TYPEDEF_(0x80410009L) +#endif + +#ifndef QUIC_TLS_ALERT_HRESULT_PREFIX +#define QUIC_TLS_ALERT_HRESULT_PREFIX _HRESULT_TYPEDEF_(0x80410100L) +#endif + +#define QUIC_API __cdecl +#define QUIC_MAIN_EXPORT __cdecl +#define QUIC_STATUS HRESULT +#define QUIC_FAILED(X) FAILED(X) +#define QUIC_SUCCEEDED(X) SUCCEEDED(X) + +#define QUIC_STATUS_SUCCESS S_OK // 0x0 +#define QUIC_STATUS_PENDING SUCCESS_HRESULT_FROM_WIN32(ERROR_IO_PENDING) // 0x703e5 +#define QUIC_STATUS_CONTINUE SUCCESS_HRESULT_FROM_WIN32(ERROR_CONTINUE) // 0x704de +#define QUIC_STATUS_OUT_OF_MEMORY E_OUTOFMEMORY // 0x8007000e +#define QUIC_STATUS_INVALID_PARAMETER E_INVALIDARG // 0x80070057 +#define QUIC_STATUS_INVALID_STATE E_NOT_VALID_STATE // 0x8007139f +#define QUIC_STATUS_NOT_SUPPORTED E_NOINTERFACE // 0x80004002 +#define QUIC_STATUS_NOT_FOUND HRESULT_FROM_WIN32(ERROR_NOT_FOUND) // 0x80070490 +#define QUIC_STATUS_BUFFER_TOO_SMALL E_NOT_SUFFICIENT_BUFFER // 0x8007007a +#define QUIC_STATUS_HANDSHAKE_FAILURE ERROR_QUIC_HANDSHAKE_FAILURE // 0x80410000 +#define QUIC_STATUS_ABORTED E_ABORT // 0x80004004 +#define QUIC_STATUS_ADDRESS_IN_USE HRESULT_FROM_WIN32(WSAEADDRINUSE) // 0x80072740 +#define QUIC_STATUS_INVALID_ADDRESS HRESULT_FROM_WIN32(WSAEADDRNOTAVAIL) // 0x80072741 +#define QUIC_STATUS_CONNECTION_TIMEOUT ERROR_QUIC_CONNECTION_TIMEOUT // 0x80410006 +#define QUIC_STATUS_CONNECTION_IDLE ERROR_QUIC_CONNECTION_IDLE // 0x80410005 +#define QUIC_STATUS_UNREACHABLE HRESULT_FROM_WIN32(ERROR_HOST_UNREACHABLE) // 0x800704d0 +#define QUIC_STATUS_INTERNAL_ERROR ERROR_QUIC_INTERNAL_ERROR // 0x80410003 +#define QUIC_STATUS_CONNECTION_REFUSED HRESULT_FROM_WIN32(ERROR_CONNECTION_REFUSED) // 0x800704c9 +#define QUIC_STATUS_PROTOCOL_ERROR ERROR_QUIC_PROTOCOL_VIOLATION // 0x80410004 +#define QUIC_STATUS_VER_NEG_ERROR ERROR_QUIC_VER_NEG_FAILURE // 0x80410001 +#define QUIC_STATUS_TLS_ERROR HRESULT_FROM_WIN32(WSA_SECURE_HOST_NOT_FOUND) // 0x80072b18 +#define QUIC_STATUS_USER_CANCELED ERROR_QUIC_USER_CANCELED // 0x80410002 +#define QUIC_STATUS_ALPN_NEG_FAILURE ERROR_QUIC_ALPN_NEG_FAILURE // 0x80410007 +#define QUIC_STATUS_STREAM_LIMIT_REACHED ERROR_QUIC_STREAM_LIMIT_REACHED // 0x80410008 +#define QUIC_STATUS_ALPN_IN_USE ERROR_QUIC_ALPN_IN_USE // 0x80410009 + +#define QUIC_STATUS_TLS_ALERT(Alert) (QUIC_TLS_ALERT_HRESULT_PREFIX | (0xff & Alert)) + +#define QUIC_STATUS_CLOSE_NOTIFY QUIC_STATUS_TLS_ALERT(0) // Close notify +#define QUIC_STATUS_BAD_CERTIFICATE QUIC_STATUS_TLS_ALERT(42) // Bad Certificate +#define QUIC_STATUS_UNSUPPORTED_CERTIFICATE QUIC_STATUS_TLS_ALERT(43) // Unsupported Certficiate +#define QUIC_STATUS_REVOKED_CERTIFICATE QUIC_STATUS_TLS_ALERT(44) // Revoked Certificate +#define QUIC_STATUS_EXPIRED_CERTIFICATE QUIC_STATUS_TLS_ALERT(45) // Expired Certificate +#define QUIC_STATUS_UNKNOWN_CERTIFICATE QUIC_STATUS_TLS_ALERT(46) // Unknown Certificate +#define QUIC_STATUS_REQUIRED_CERTIFICATE QUIC_STATUS_TLS_ALERT(116) // Required Certificate + +#define QUIC_STATUS_CERT_EXPIRED CERT_E_EXPIRED +#define QUIC_STATUS_CERT_UNTRUSTED_ROOT CERT_E_UNTRUSTEDROOT +#define QUIC_STATUS_CERT_NO_CERT SEC_E_NO_CREDENTIALS + +// +// Swaps byte orders between host and network endianness. +// +#ifdef htons +#define QuicNetByteSwapShort(x) htons(x) +#else +#define QuicNetByteSwapShort(x) ((uint16_t)((((x) & 0x00ff) << 8) | (((x) & 0xff00) >> 8))) +#endif + +// +// IP Address Abstraction Helpers +// + +typedef ADDRESS_FAMILY QUIC_ADDRESS_FAMILY; +typedef SOCKADDR_INET QUIC_ADDR; + +#define QUIC_ADDR_V4_PORT_OFFSET FIELD_OFFSET(SOCKADDR_IN, sin_port) +#define QUIC_ADDR_V4_IP_OFFSET FIELD_OFFSET(SOCKADDR_IN, sin_addr) + +#define QUIC_ADDR_V6_PORT_OFFSET FIELD_OFFSET(SOCKADDR_IN6, sin6_port) +#define QUIC_ADDR_V6_IP_OFFSET FIELD_OFFSET(SOCKADDR_IN6, sin6_addr) + +#define QUIC_ADDRESS_FAMILY_UNSPEC AF_UNSPEC +#define QUIC_ADDRESS_FAMILY_INET AF_INET +#define QUIC_ADDRESS_FAMILY_INET6 AF_INET6 + +inline +BOOLEAN +QuicAddrIsValid( + _In_ const QUIC_ADDR* const Addr + ) +{ + return + Addr->si_family == QUIC_ADDRESS_FAMILY_UNSPEC || + Addr->si_family == QUIC_ADDRESS_FAMILY_INET || + Addr->si_family == QUIC_ADDRESS_FAMILY_INET6; +} + +inline +BOOLEAN +QuicAddrCompareIp( + _In_ const QUIC_ADDR* const Addr1, + _In_ const QUIC_ADDR* const Addr2 + ) +{ + if (Addr1->si_family == QUIC_ADDRESS_FAMILY_INET) { + return memcmp(&Addr1->Ipv4.sin_addr, &Addr2->Ipv4.sin_addr, sizeof(IN_ADDR)) == 0; + } else { + return memcmp(&Addr1->Ipv6.sin6_addr, &Addr2->Ipv6.sin6_addr, sizeof(IN6_ADDR)) == 0; + } +} + +inline +BOOLEAN +QuicAddrCompare( + _In_ const QUIC_ADDR* const Addr1, + _In_ const QUIC_ADDR* const Addr2 + ) +{ + if (Addr1->si_family != Addr2->si_family || + Addr1->Ipv4.sin_port != Addr2->Ipv4.sin_port) { + return FALSE; + } + return QuicAddrCompareIp(Addr1, Addr2); +} + +inline +BOOLEAN +QuicAddrIsWildCard( + _In_ const QUIC_ADDR* const Addr + ) +{ + if (Addr->si_family == QUIC_ADDRESS_FAMILY_UNSPEC) { + return TRUE; + } else if (Addr->si_family == QUIC_ADDRESS_FAMILY_INET) { + const IN_ADDR ZeroAddr = {0}; + return memcmp(&Addr->Ipv4.sin_addr, &ZeroAddr, sizeof(IN_ADDR)) == 0; + } else { + const IN6_ADDR ZeroAddr = {0}; + return memcmp(&Addr->Ipv6.sin6_addr, &ZeroAddr, sizeof(IN6_ADDR)) == 0; + } +} + +inline +QUIC_ADDRESS_FAMILY +QuicAddrGetFamily( + _In_ const QUIC_ADDR* const Addr + ) +{ + return (QUIC_ADDRESS_FAMILY)Addr->si_family; +} + +inline +void +QuicAddrSetFamily( + _Inout_ QUIC_ADDR* Addr, + _In_ QUIC_ADDRESS_FAMILY Family + ) +{ + Addr->si_family = (ADDRESS_FAMILY)Family; +} + +inline +uint16_t // Returns in host byte order. +QuicAddrGetPort( + _In_ const QUIC_ADDR* const Addr + ) +{ + return QuicNetByteSwapShort(Addr->Ipv4.sin_port); +} + +inline +void +QuicAddrSetPort( + _Out_ QUIC_ADDR* Addr, + _In_ uint16_t Port // Host byte order + ) +{ + Addr->Ipv4.sin_port = QuicNetByteSwapShort(Port); +} + +inline +void +QuicAddrSetToLoopback( + _Inout_ QUIC_ADDR* Addr + ) +{ + if (Addr->si_family == QUIC_ADDRESS_FAMILY_INET) { + Addr->Ipv4.sin_addr.S_un.S_un_b.s_b1 = 127; + Addr->Ipv4.sin_addr.S_un.S_un_b.s_b4 = 1; + } else { + Addr->Ipv6.sin6_addr.u.Byte[15] = 1; + } +} + +// +// Test only API to increment the IP address value. +// +inline +void +QuicAddrIncrement( + _Inout_ QUIC_ADDR* Addr + ) +{ + if (Addr->si_family == QUIC_ADDRESS_FAMILY_INET) { + Addr->Ipv4.sin_addr.S_un.S_un_b.s_b4++; + } else { + Addr->Ipv6.sin6_addr.u.Byte[15]++; + } +} + +inline +uint32_t +QuicAddrHash( + _In_ const QUIC_ADDR* Addr + ) +{ + uint32_t Hash = 5387; // A random prime number. +#define UPDATE_HASH(byte) Hash = ((Hash << 5) - Hash) + (byte) + if (Addr->si_family == QUIC_ADDRESS_FAMILY_INET) { + UPDATE_HASH(Addr->Ipv4.sin_port & 0xFF); + UPDATE_HASH(Addr->Ipv4.sin_port >> 8); + for (uint8_t i = 0; i < sizeof(Addr->Ipv4.sin_addr); ++i) { + UPDATE_HASH(((uint8_t*)&Addr->Ipv4.sin_addr)[i]); + } + } else { + UPDATE_HASH(Addr->Ipv6.sin6_port & 0xFF); + UPDATE_HASH(Addr->Ipv6.sin6_port >> 8); + for (uint8_t i = 0; i < sizeof(Addr->Ipv6.sin6_addr); ++i) { + UPDATE_HASH(((uint8_t*)&Addr->Ipv6.sin6_addr)[i]); + } + } + return Hash; +} + +#define QUIC_LOCALHOST_FOR_AF(Af) "localhost" + +// +// Rtl String API's are not allowed in gamecore +// +#if WINAPI_FAMILY != WINAPI_FAMILY_GAMES + +inline +_Success_(return != FALSE) +BOOLEAN +QuicAddrFromString( + _In_z_ const char* AddrStr, + _In_ uint16_t Port, // Host byte order + _Out_ QUIC_ADDR* Addr + ) +{ + if (RtlIpv4StringToAddressExA(AddrStr, FALSE, &Addr->Ipv4.sin_addr, &Addr->Ipv4.sin_port) == NO_ERROR) { + Addr->si_family = QUIC_ADDRESS_FAMILY_INET; + } else if (RtlIpv6StringToAddressExA(AddrStr, &Addr->Ipv6.sin6_addr, &Addr->Ipv6.sin6_scope_id, &Addr->Ipv6.sin6_port) == NO_ERROR) { + Addr->si_family = QUIC_ADDRESS_FAMILY_INET6; + } else { + return FALSE; + } + if (Addr->Ipv4.sin_port == 0) { + Addr->Ipv4.sin_port = QuicNetByteSwapShort(Port); + } + return TRUE; +} + +// +// Represents an IP address and (optionally) port number as a string. +// +typedef struct QUIC_ADDR_STR { + char Address[64]; +} QUIC_ADDR_STR; + +inline +_Success_(return != FALSE) +BOOLEAN +QuicAddrToString( + _In_ const QUIC_ADDR* Addr, + _Out_ QUIC_ADDR_STR* AddrStr + ) +{ + LONG Status; + ULONG AddrStrLen = ARRAYSIZE(AddrStr->Address); + if (Addr->si_family == QUIC_ADDRESS_FAMILY_INET) { + Status = + RtlIpv4AddressToStringExA( + &Addr->Ipv4.sin_addr, + Addr->Ipv4.sin_port, + AddrStr->Address, + &AddrStrLen); + } else { + Status = + RtlIpv6AddressToStringExA( + &Addr->Ipv6.sin6_addr, + 0, + Addr->Ipv6.sin6_port, + AddrStr->Address, + &AddrStrLen); + } + return Status == NO_ERROR; +} + +#endif // WINAPI_FAMILY != WINAPI_FAMILY_GAMES + +#endif // _MSQUIC_WINUSER_ diff --git a/msquic/lib/msquic.lib b/msquic/lib/msquic.lib new file mode 100644 index 0000000..1a9d4e0 Binary files /dev/null and b/msquic/lib/msquic.lib differ