加入MsQuic库

main
wuyize 2023-06-19 15:45:16 +08:00
parent 95d3698189
commit 1cf18736bb
14 changed files with 3641 additions and 31 deletions

3
.gitignore vendored
View File

@ -363,4 +363,5 @@ MigrationBackup/
FodyWeavers.xsd FodyWeavers.xsd
cmake-build-*/ cmake-build-*/
.idea/ .idea/
CMakeSettings.json

View File

@ -9,46 +9,57 @@ find_package(Qt6 COMPONENTS Quick REQUIRED)
#Cpp #Cpp
file(GLOB_RECURSE CPP_FILES src/*.cpp src/*.h) 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}) string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/" "" filename ${filepath})
list(APPEND sources_files ${filename}) list(APPEND sources_files ${filename})
endforeach(filepath) endforeach (filepath)
#qml #qml
file(GLOB_RECURSE QML_PATHS qml/*.qml) file(GLOB_RECURSE QML_PATHS qml/*.qml)
foreach(filepath ${QML_PATHS}) foreach (filepath ${QML_PATHS})
string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/" "" filename ${filepath}) string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/" "" filename ${filepath})
list(APPEND qml_files ${filename}) 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) 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}) string(REPLACE "${CMAKE_CURRENT_SOURCE_DIR}/" "" filename ${filepath})
list(APPEND resource_files ${filename}) list(APPEND resource_files ${filename})
endforeach(filepath) endforeach (filepath)
qt_add_executable(AicsKnowledgeBase qt_add_executable(AicsKnowledgeBase
${sources_files} ${sources_files}
) )
qt_add_qml_module(AicsKnowledgeBase qt_add_qml_module(AicsKnowledgeBase
URI AicsKnowledgeBase URI AicsKnowledgeBase
VERSION 1.0 VERSION 1.0
QML_FILES ${qml_files} QML_FILES ${qml_files}
RESOURCES ${resource_files} RESOURCES ${resource_files}
) )
set_target_properties(AicsKnowledgeBase PROPERTIES set_target_properties(AicsKnowledgeBase PROPERTIES
WIN32_EXECUTABLE TRUE WIN32_EXECUTABLE TRUE
) )
target_compile_definitions(AicsKnowledgeBase target_compile_definitions(AicsKnowledgeBase
PRIVATE $<$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>:QT_QML_DEBUG>) PRIVATE $<$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>:QT_QML_DEBUG>)
target_include_directories(AicsKnowledgeBase PRIVATE
../msquic/include)
target_link_libraries(AicsKnowledgeBase PRIVATE target_link_libraries(AicsKnowledgeBase PRIVATE
Qt6::Quick Qt6::Quick
fluentuiplugin fluentuiplugin
FramelessHelper::Core FramelessHelper::Core
FramelessHelper::Quick 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}
)

View File

@ -4,6 +4,7 @@ import QtQuick.Controls
import QtQuick.Window import QtQuick.Window
import QtQuick.Layouts import QtQuick.Layouts
import org.wangwenx190.FramelessHelper import org.wangwenx190.FramelessHelper
import AicsKB.HttpClient
Row { Row {
anchors.fill: parent anchors.fill: parent
@ -27,16 +28,21 @@ Row {
width: parent.width * 0.5 width: parent.width * 0.5
height: parent.height height: parent.height
FluPivot { FluPivot {
id: loginItem
Layout.fillWidth: true Layout.fillWidth: true
Layout.alignment: Qt.AlignVCenter Layout.alignment: Qt.AlignVCenter
Layout.margins: 20 Layout.margins: 20
height: 200 height: 200
FluPivotItem { FluPivotItem {
id: loginItem
title: "登录" title: "登录"
function login(s)
{
showSuccess(s);
}
contentItem: Column { contentItem: Column {
anchors.margins: { anchors.margins: {
top: 10 top: 10
@ -44,7 +50,6 @@ Row {
anchors.top: parent.top anchors.top: parent.top
spacing: 12 spacing: 12
FluTextBox { FluTextBox {
anchors.left: parent.left anchors.left: parent.left
anchors.right: parent.right anchors.right: parent.right
@ -76,8 +81,7 @@ Row {
normalColor: FluColors.Green.normal normalColor: FluColors.Green.normal
text: "登录" text: "登录"
onClicked: { onClicked: {
showSuccess("click") HttpClient.doGetRequest(loginItem, "login");
} }
} }
} }
@ -91,6 +95,4 @@ Row {
} }
} }
} }
} }

View File

@ -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"));
}

View File

@ -0,0 +1,24 @@
//
// Created by wuyiz on 2023/6/19.
//
#ifndef AICSKNOWLEDGEBASE_HTTPCLIENT_H
#define AICSKNOWLEDGEBASE_HTTPCLIENT_H
#include <msquic.h>
#include <QObject>
#include <QQmlApplicationEngine>
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

View File

@ -6,6 +6,7 @@
#include <QProcess> #include <QProcess>
#include <FramelessHelper/Quick/framelessquickmodule.h> #include <FramelessHelper/Quick/framelessquickmodule.h>
#include <FramelessHelper/Core/private/framelessconfig_p.h> #include <FramelessHelper/Core/private/framelessconfig_p.h>
#include "HttpClient.h"
FRAMELESSHELPER_USE_NAMESPACE FRAMELESSHELPER_USE_NAMESPACE
@ -15,6 +16,7 @@ int main(int argc, char* argv[])
qputenv("QT_QUICK_CONTROLS_STYLE", "Basic"); qputenv("QT_QUICK_CONTROLS_STYLE", "Basic");
FramelessHelper::Quick::initialize(); FramelessHelper::Quick::initialize();
QGuiApplication app(argc, argv); QGuiApplication app(argc, argv);
#ifdef Q_OS_WIN // 此设置仅在Windows下生效 #ifdef Q_OS_WIN // 此设置仅在Windows下生效
FramelessConfig::instance()->set(Global::Option::ForceHideWindowFrameBorder); FramelessConfig::instance()->set(Global::Option::ForceHideWindowFrameBorder);
#endif #endif
@ -28,12 +30,18 @@ int main(int argc, char* argv[])
QQmlApplicationEngine engine; QQmlApplicationEngine engine;
FramelessHelper::Quick::registerTypes(&engine); FramelessHelper::Quick::registerTypes(&engine);
const QUrl url(u"qrc:/AicsKnowledgeBase/qml/App.qml"_qs); HttpClient* httpClient = new HttpClient(engine);
qmlRegisterSingletonInstance<HttpClient>("AicsKB.HttpClient", 1, 0, "HttpClient", httpClient);
const QUrl url(u"qrc:/AicsKnowledgeBase/qml/App.qml"_qs);
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject* obj, const QUrl& objUrl) { &app, [url](QObject* obj, const QUrl& objUrl) {
if (!obj && url == objUrl) if (!obj && url == objUrl)
QCoreApplication::exit(-1); QCoreApplication::exit(-1);
}, Qt::QueuedConnection); }, Qt::QueuedConnection);
engine.load(url); engine.load(url);
return app.exec(); auto result = app.exec();
httpClient->deleteLater();
return result;
} }

View File

@ -3,4 +3,8 @@ cmake_minimum_required(VERSION 3.20)
project(AicsKnowledgeBase LANGUAGES CXX) project(AicsKnowledgeBase LANGUAGES CXX)
add_subdirectory(FluentUI) add_subdirectory(FluentUI)
link_directories(msquic/lib)
link_directories(msquic/bin)
add_subdirectory(AicsKnowledgeBase) add_subdirectory(AicsKnowledgeBase)
add_subdirectory(MsQuicSample)

View File

@ -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}
)

907
MsQuicSample/sample.cpp Normal file
View File

@ -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 <stdio.h>
#include <stdlib.h>
#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:<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;
}

21
msquic/LICENSE Normal file
View File

@ -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.

187
msquic/THIRD-PARTY-NOTICES Normal file
View File

@ -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

1640
msquic/include/msquic.h Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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 <windows.h>
#pragma warning(pop)
#include <winsock2.h>
#include <ws2ipdef.h>
#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 <ws2tcpip.h>
#include <mstcpip.h>
#pragma warning(pop)
#include <stdint.h>
#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_

BIN
msquic/lib/msquic.lib Normal file

Binary file not shown.