├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── Makefile.am ├── README.md ├── autogen.sh ├── configure.ac ├── docs ├── Makefile.am ├── doxy-boot.js ├── favicon.ico ├── footer.html ├── header-api.html ├── header-internal.html ├── header.html ├── imquic-api.cfg ├── imquic-internal.cfg ├── imquic.cfg ├── imquic.css ├── mainpage-api.dox ├── mainpage-internal.dox ├── mainpage.dox └── meetecho-logo.png ├── examples ├── Makefile.am ├── README.md ├── echo-client-options.c ├── echo-client-options.h ├── echo-client.c ├── echo-server-options.c ├── echo-server-options.h ├── echo-server.c ├── moq-pub-options.c ├── moq-pub-options.h ├── moq-pub.c ├── moq-relay-options.c ├── moq-relay-options.h ├── moq-relay.c ├── moq-sub-options.c ├── moq-sub-options.h ├── moq-sub.c ├── moq-test-options.c ├── moq-test-options.h ├── moq-test.c ├── moq-utils.c ├── moq-utils.h ├── roq-client-options.c ├── roq-client-options.h ├── roq-client.c ├── roq-server-options.c ├── roq-server-options.h └── roq-server.c ├── imquic.pc.in └── src ├── Makefile.am ├── buffer.c ├── connection.c ├── crypto.c ├── error.c ├── http3.c ├── imquic-moq.c ├── imquic-roq.c ├── imquic.c ├── imquic ├── debug.h ├── imquic.h ├── moq.h └── roq.h ├── internal ├── buffer.h ├── configuration.h ├── connection.h ├── crypto.h ├── error.h ├── http3.h ├── huffman.h ├── listmap.h ├── loop.h ├── moq.h ├── mutex.h ├── network.h ├── qlog.h ├── qpack.h ├── quic.h ├── refcount.h ├── roq.h ├── stream.h ├── utils.h └── version.h ├── listmap.c ├── loop.c ├── moq.c ├── network.c ├── qlog.c ├── qpack.c ├── quic.c ├── roq.c ├── stream.c └── utils.c /.gitignore: -------------------------------------------------------------------------------- 1 | imquic-echo-client 2 | imquic-echo-server 3 | imquic-roq-client 4 | imquic-roq-server 5 | imquic-moq-pub 6 | imquic-moq-sub 7 | imquic-moq-chat 8 | imquic-moq-test 9 | imquic-moq-relay 10 | 11 | version.c 12 | docs/html/ 13 | 14 | Makefile 15 | Makefile.in 16 | build 17 | configure 18 | configure~ 19 | autom4te.cache 20 | aclocal.m4 21 | m4 22 | missing 23 | libtool 24 | depcomp 25 | compile 26 | install-sh 27 | install-sh~ 28 | ltmain.sh 29 | 30 | config.log 31 | config.guess 32 | config.status 33 | config.sub 34 | 35 | imquic.pc 36 | 37 | *.o 38 | *.a 39 | *.lo 40 | *.la 41 | .libs 42 | 43 | .deps 44 | .dirstamp 45 | 46 | # OS X 47 | .DS_Store 48 | 49 | # CLion 50 | .idea 51 | 52 | .vscode 53 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | 6 | ## [v0.0.1] - 2024-XX-XX 7 | 8 | - First alpha release 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024-2025 Meetecho 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | ACLOCAL_AMFLAGS = -I m4 2 | 3 | # FIXME: make docs work with distcheck 4 | DISTCHECK_CONFIGURE_FLAGS = --disable-docs 5 | 6 | EXTRA_DIST = imquic.pc.in 7 | pkgconfigdir = $(libdir)/pkgconfig 8 | pkgconfig_DATA = imquic.pc 9 | DISTCLEANFILES = imquic.pc 10 | 11 | SUBDIRS = src examples docs 12 | #~ dist_html_DATA = README.md 13 | 14 | .PHONY: FORCE 15 | FORCE: 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | imquic 2 | ====== 3 | 4 | imquic is an open source QUIC library designed and developed by [Meetecho](https://www.meetecho.com) for the specific purpose of experimenting with QUIC-based multimedia applications. While it can be used as a generic QUIC (and WebTransport) library, it also comes with experimental native RTP Over QUIC (RoQ) and Media Over QUIC (MoQ) support. At the time of writing, there's no support for HTTP/3 beyond the simple establishment of WebTransport connections. 5 | 6 | For more information and documentations, make sure you pay the [project website](https://imquic.conf.meetecho.com) a visit! 7 | 8 | > **Note well:** in its current stage, the library should be considered at an **alpha** stage, and very experimental due to its lack of support for some QUIC stack functionality. It is currently being used by Meetecho for prototyping RoQ and MoQ demos in local and controlled environments, and will probably not always work as expected in more challenging network scenarios. 9 | 10 | ## Dependencies 11 | 12 | To compile imquic, you'll need to satisfy the following dependencies: 13 | 14 | * [GLib](https://docs.gtk.org/glib/) 15 | * [pkg-config](http://www.freedesktop.org/wiki/Software/pkg-config/) 16 | * [quictls](https://quictls.github.io/) (QUIC TLS) 17 | * [Jansson](https://github.com/akheron/jansson) (optional; QLOG support) 18 | 19 | > **Note:** You can also use BoringSSL, instead of quictls, by passing `--enable-boringssl=`, albeit without early-data support. If BoringSSL is installed in `/opt/boringssl`, the configure script will expect header files in `/opt/boringssl/include` and shared (not static) objects in `/opt/boringssl/lib64`. You'll then need to manually export `LD_LIBRARY_PATH` to the path where BoringSSL shared objects are, when using an application that's linked to the library. 20 | 21 | Should you be interested in building the imquic documentation as well (public and internal), you'll need some additional tools too: 22 | 23 | * [Doxygen](https://www.doxygen.org) 24 | * [Graphviz](https://www.graphviz.org/) 25 | 26 | Notice that, at the time of writing, only Linux is supported as a target, but the library should compile on macOS as well. Both macOS and Windows, as well as other platforms, are planned as a target in future releases. 27 | 28 | ## Compile 29 | 30 | Once you have installed all the dependencies, just use: 31 | 32 | sh autogen.sh 33 | 34 | to generate the configure file. After that, configure and compile as usual to start the whole compilation process: 35 | 36 | ./configure --prefix=/usr 37 | make 38 | make install 39 | 40 | Note that the configure script uses `pkg-config` to look for quictls by using the `openssl+quictls` name, which is how it's packaged in some repositories to avoid conflicts with OpenSSL. In case that doesn't work for you, you can use the `QUICTLS_CFLAGS` and `QUICTLS_LIBS` environment variables to specify the include and lib directory of the library when launching the configure script, e.g. 41 | 42 | QUICTLS_CFLAGS="-I/opt/quictls/include/" \ 43 | QUICTLS_LIBS="-L/opt/quictls/lib64/ -lssl -lcrypto" \ 44 | ./configure [..] 45 | 46 | Should that still result in compilation issues, you can try adding the path to the quictls library to ld, which is what the above mentioned repositories do, e.g. 47 | 48 | echo "/opt/quictls/lib64" > /etc/ld.so.conf.d/quictls-x86_64.conf 49 | ldconfig 50 | 51 | If you're interested in QLOG support, add `--enable-qlog` when launching the `configure` script. Notice that this will require [Jansson](https://github.com/akheron/jansson). 52 | 53 | You can build some demo applications by adding `--enable-echo-examples` (basic QUIC/WebTransport client/server demos), `--enable-roq-examples` (RoQ demos) and `--enable-moq-examples` (MoQ demos). 54 | 55 | To build the documentation, add `--enable-docs`. 56 | 57 | ## Examples 58 | 59 | To learn more about the demo examples, refer to the related [README.md](examples/README.md). 60 | 61 | ## Help us! 62 | Any thought, feedback or (hopefully not!) insult is welcome! 63 | 64 | Developed by [@meetecho](https://github.com/meetecho) 65 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | srcdir=`dirname $0` 4 | test -z "$srcdir" && srcdir=. 5 | 6 | mkdir -p m4 7 | 8 | autoreconf --verbose --force --install || exit 1 9 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT([imquic],[0.0.1],[https://github.com/meetecho/imquic],[imquic],[https://imquic.conf.meetecho.com]) 2 | AC_LANG(C) 3 | AC_CONFIG_AUX_DIR([.]) 4 | AC_CONFIG_MACRO_DIR([m4]) 5 | 6 | AC_ENABLE_SHARED(yes) 7 | AC_ENABLE_STATIC(no) 8 | 9 | AM_INIT_AUTOMAKE([foreign subdir-objects]) 10 | AM_SILENT_RULES([yes]) 11 | 12 | AC_USE_SYSTEM_EXTENSIONS 13 | 14 | AC_PROG_CC 15 | 16 | LT_PREREQ([2.2]) 17 | LT_INIT 18 | 19 | # Common CFLAGS 20 | CFLAGS="$CFLAGS \ 21 | -fPIC \ 22 | -fstack-protector-all \ 23 | -fstrict-aliasing \ 24 | -pthread \ 25 | -Wall \ 26 | -Warray-bounds \ 27 | -Wextra \ 28 | -Wformat-nonliteral \ 29 | -Wformat-security \ 30 | -Wformat=2 \ 31 | -Winit-self \ 32 | -Wlarger-than=2097152 \ 33 | -Wmissing-declarations \ 34 | -Wmissing-format-attribute \ 35 | -Wmissing-include-dirs \ 36 | -Wmissing-noreturn \ 37 | -Wmissing-prototypes \ 38 | -Wnested-externs \ 39 | -Wold-style-definition \ 40 | -Wpacked \ 41 | -Wpointer-arith \ 42 | -Wsign-compare \ 43 | -Wstrict-prototypes \ 44 | -Wswitch-default \ 45 | -Wunused \ 46 | -Wno-unused-parameter \ 47 | -Wno-unused-result \ 48 | -Wwrite-strings \ 49 | -Werror=implicit-function-declaration" 50 | 51 | case "$CC" in 52 | *clang*) 53 | # Specific clang flags 54 | CFLAGS="$CFLAGS \ 55 | -Wno-initializer-overrides \ 56 | -Wno-missing-noreturn" 57 | ;; 58 | cc*) 59 | CFLAGS="$CFLAGS \ 60 | -Wno-cast-align \ 61 | -Wno-initializer-overrides" 62 | ;; 63 | *) 64 | # Specific gcc flags 65 | CFLAGS="$CFLAGS \ 66 | -Wno-override-init \ 67 | -Wunsafe-loop-optimizations \ 68 | -Wunused-but-set-variable" 69 | esac 70 | 71 | IMQUIC_VERSION_MAJOR=0 72 | AC_SUBST(IMQUIC_VERSION_MAJOR) 73 | IMQUIC_VERSION_MINOR=0 74 | AC_SUBST(IMQUIC_VERSION_MINOR) 75 | IMQUIC_VERSION_PATCH=1 76 | AC_SUBST(IMQUIC_VERSION_PATCH) 77 | IMQUIC_VERSION_STRING="0.0.1" 78 | AC_SUBST(IMQUIC_VERSION_STRING) 79 | IMQUIC_VERSION_RELEASE="alpha" 80 | AC_SUBST(IMQUIC_VERSION_RELEASE) 81 | IMQUIC_VERSION_SO="0:0:0" 82 | AC_SUBST(IMQUIC_VERSION_SO) 83 | 84 | glib_version=2.34 85 | 86 | AC_ARG_ENABLE([boringssl], 87 | [AS_HELP_STRING([--enable-boringssl], 88 | [Use BoringSSL instead of OpenSSL])], 89 | [ 90 | case "${enableval}" in 91 | yes) boringssl_dir=/opt/boringssl ;; 92 | no) boringssl_dir= ;; 93 | *) boringssl_dir=${enableval} ;; 94 | esac 95 | ], 96 | [boringssl_dir=]) 97 | 98 | PKG_CHECK_MODULES([IMQUIC],[ 99 | glib-2.0 >= $glib_version 100 | ]) 101 | 102 | IMQUIC_PACKAGES_PUBLIC="glib-2.0 >= $glib_version" 103 | IMQUIC_PACKAGES_PRIVATE="" 104 | 105 | IMQUIC_MANUAL_LIBS="${IMQUIC_MANUAL_LIBS} -lm" 106 | AC_SUBST(IMQUIC_MANUAL_LIBS) 107 | 108 | AS_IF([test "x${boringssl_dir}" != "x"], 109 | [echo "Trying to use BoringSSL instead of quictls..."; 110 | AC_MSG_NOTICE([BoringSSL directory is ${boringssl_dir}]) 111 | CFLAGS="$CFLAGS -I${boringssl_dir}/include"; 112 | BORINGSSL_CFLAGS="-I${boringssl_dir}/include"; 113 | AC_SUBST(BORINGSSL_CFLAGS) 114 | BORINGSSL_LIBS="-lstdc++ -L${boringssl_dir}/lib64 -lssl -lcrypto"; 115 | AC_SUBST(BORINGSSL_LIBS) 116 | AC_CHECK_HEADERS([${boringssl_dir}/include/openssl/opensslconf.h], 117 | [AC_DEFINE(IMQUIC_BORINGSSL)], 118 | [AC_MSG_ERROR([BoringSSL headers not found in ${boringssl_dir}])]) 119 | ], 120 | [ 121 | PKG_CHECK_MODULES([QUICTLS],[openssl+quictls]) 122 | ]) 123 | AM_CONDITIONAL([ENABLE_BORINGSSL], [test "x${boringssl_dir}" != "x"]) 124 | 125 | AC_ARG_ENABLE([qlog], 126 | [AS_HELP_STRING([--enable-qlog], 127 | [Enable QLOG support (requires Jansson)])], 128 | [], 129 | [enable_qlog=no]) 130 | AM_CONDITIONAL([ENABLE_QLOG], false) 131 | AS_IF([test "x$enable_qlog" != "xno"], 132 | [PKG_CHECK_MODULES([JANSSON], 133 | [jansson >= 2.5.0], 134 | [ 135 | AC_DEFINE(HAVE_QLOG) 136 | enable_qlog=yes 137 | AM_CONDITIONAL([ENABLE_QLOG], true) 138 | IMQUIC_PACKAGES_PRIVATE="jansson >= 2.5.0" 139 | ], 140 | [ 141 | AC_MSG_ERROR([Jansson headers not found])]) 142 | ]) 143 | 144 | AC_SUBST(IMQUIC_PACKAGES_PUBLIC) 145 | AC_SUBST(IMQUIC_PACKAGES_PRIVATE) 146 | 147 | ## 148 | # Examples 149 | ## 150 | 151 | AC_ARG_ENABLE([echo-examples], 152 | [AS_HELP_STRING([--enable-echo-examples], 153 | [Build the QUIC echo server and client examples])], 154 | [], 155 | [enable_echo_examples=no]) 156 | AM_CONDITIONAL([ENABLE_ECHO_EXAMPLES], [test "x$enable_echo_examples" = "xyes"]) 157 | 158 | AC_ARG_ENABLE([moq-examples], 159 | [AS_HELP_STRING([--enable-moq-examples], 160 | [Build the MoQ (Media Over QUIC) examples])], 161 | [], 162 | [enable_moq_examples=no]) 163 | AM_CONDITIONAL([ENABLE_MOQ_EXAMPLES], [test "x$enable_moq_examples" = "xyes"]) 164 | 165 | AC_ARG_ENABLE([roq-examples], 166 | [AS_HELP_STRING([--enable-roq-examples], 167 | [Build the RoQ (RTP Over QUIC) examples])], 168 | [], 169 | [enable_roq_examples=no]) 170 | AM_CONDITIONAL([ENABLE_ROQ_EXAMPLES], [test "x$enable_roq_examples" = "xyes"]) 171 | 172 | ## 173 | # Docs 174 | ## 175 | 176 | AC_ARG_ENABLE([docs], 177 | [AS_HELP_STRING([--enable-docs], 178 | [Enable building documentation])], 179 | [], 180 | [enable_docs=no]) 181 | 182 | AC_CHECK_PROG([DOXYGEN], 183 | [doxygen], 184 | [doxygen]) 185 | AC_CHECK_PROG([DOT], 186 | [dot], 187 | [dot]) 188 | AS_IF([test -z "$DOXYGEN" -o -z "$DOT"], 189 | [ 190 | AS_IF([test "x$enable_docs" = "xyes"], 191 | [AC_MSG_ERROR([doxygen or dot not found. See README.md for installation instructions or remove --enable-docs])]) 192 | ]) 193 | AM_CONDITIONAL([ENABLE_DOCS], [test "x$enable_docs" = "xyes"]) 194 | if test "x$enable_docs" = "xyes"; then 195 | doxygen_version=$($DOXYGEN --version) 196 | AS_VERSION_COMPARE([$doxygen_version], [1.8.11], 197 | [], 198 | [], 199 | [ 200 | AS_VERSION_COMPARE([$doxygen_version], [1.8.14], 201 | [AC_MSG_ERROR([Doxygen $doxygen_version not usable: versions between 1.8.12 and 1.8.14 are known to render poorly.])], 202 | [], 203 | [] 204 | ) 205 | ] 206 | ) 207 | fi 208 | 209 | AM_CONDITIONAL([WITH_SOURCE_DATE_EPOCH], [test "x$SOURCE_DATE_EPOCH" != "x"]) 210 | 211 | ## 212 | # Summary 213 | ## 214 | 215 | AC_CONFIG_FILES([ 216 | Makefile 217 | src/Makefile 218 | examples/Makefile 219 | docs/Makefile 220 | imquic.pc 221 | ]) 222 | 223 | AC_OUTPUT 224 | 225 | echo 226 | echo "Compiler: $CC" 227 | AM_COND_IF([ENABLE_BORINGSSL], 228 | [echo "Encryption: BoringSSL"], 229 | [echo "Encryption: quictls"]) 230 | AM_COND_IF([ENABLE_QLOG], 231 | [echo "QLOG Support: yes"], 232 | [echo "QLOG Support: no"]) 233 | AM_COND_IF([ENABLE_DOCS], 234 | [echo "Documentation: yes"], 235 | [echo "Documentation: no"]) 236 | echo 237 | echo "Examples:" 238 | AM_COND_IF([ENABLE_ECHO_EXAMPLES], 239 | [echo " -- Echo examples: yes"], 240 | [echo " -- Echo examples: no"]) 241 | AM_COND_IF([ENABLE_MOQ_EXAMPLES], 242 | [echo " -- MoQ examples: yes"], 243 | [echo " -- MoQ examples: no"]) 244 | AM_COND_IF([ENABLE_ROQ_EXAMPLES], 245 | [echo " -- RoQ examples: yes"], 246 | [echo " -- RoQ examples: no"]) 247 | 248 | echo 249 | echo "If this configuration is ok for you, do a 'make' to start building imquic. A 'make install' will install the library to the specified prefix." 250 | echo 251 | -------------------------------------------------------------------------------- /docs/Makefile.am: -------------------------------------------------------------------------------- 1 | if ENABLE_DOCS 2 | 3 | doxygendir = $(htmldir)/imquic-$(VERSION) 4 | 5 | EXTRA_DIST = html 6 | 7 | all: html-local 8 | 9 | html-local: 10 | mkdir -p html 11 | doxygen imquic.cfg 12 | doxygen imquic-api.cfg 13 | doxygen imquic-internal.cfg 14 | cp doxy-boot.js html/ 15 | mkdir -p html/css 16 | cp imquic.css html/css/ 17 | cp favicon.ico html/ 18 | cp meetecho-logo.png html/ 19 | 20 | install-data-local: html-local 21 | $(MKDIR_P) $(DESTDIR)$(doxygendir) 22 | cp -r html/ $(DESTDIR)$(doxygendir) 23 | 24 | uninstall-local: 25 | rm -rf $(DESTDIR)$(doxygendir) 26 | 27 | clean-local: 28 | rm -rf $(builddir)/html 29 | 30 | endif 31 | -------------------------------------------------------------------------------- /docs/doxy-boot.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | 3 | $("div.headertitle").addClass("pb-2 mt-4 mb-2 border-bottom"); 4 | $("div.title").addClass("h1"); 5 | 6 | $('li > a[href="index.html"] > span').before(" "); 7 | // $('li > a[href="index.html"] > span').text("CoActionOS"); 8 | $('li > a[href="modules.html"] > span').before(" "); 9 | $('li > a[href="namespaces.html"] > span').before(" "); 10 | $('li > a[href="annotated.html"] > span').before(" "); 11 | $('li > a[href="classes.html"] > span').before(" "); 12 | $('li > a[href="inherits.html"] > span').before(" "); 13 | $('li > a[href="functions.html"] > span').before(" "); 14 | $('li > a[href="functions_func.html"] > span').before(" "); 15 | $('li > a[href="functions_vars.html"] > span').before(" "); 16 | $('li > a[href="functions_enum.html"] > span').before(" "); 17 | $('li > a[href="functions_eval.html"] > span').before(" "); 18 | $('img[src="ftv2ns.png"]').replaceWith('N '); 19 | $('img[src="ftv2cl.png"]').replaceWith('C '); 20 | 21 | $("ul.tablist").addClass("nav nav-pills nav-fill"); 22 | $("ul.tablist").css("margin-top", "0.5em"); 23 | $("ul.tablist").css("margin-bottom", "0.5em"); 24 | $("ul.tablist > li").addClass("nav-item"); 25 | $("ul.tablist > li > a").addClass("nav-link"); 26 | $("li.current").children().addClass("active"); 27 | $("iframe").attr("scrolling", "yes"); 28 | 29 | $("#nav-path > ul").addClass("breadcrumb"); 30 | 31 | $("table.params").addClass("table"); 32 | $("div.ingroups").wrapInner(""); 33 | $("div.ingroups > small > a").addClass("text-muted"); 34 | $("div.levels").css("margin", "0.5em"); 35 | $("div.levels > span").addClass("btn btn-secondary btn-sm"); 36 | $("div.levels > span").css("margin-right", "0.25em"); 37 | 38 | $("table.directory").addClass("table table-striped"); 39 | $("div.summary > a").addClass("btn btn-secondary btn-sm"); 40 | $("table.fieldtable").addClass("table"); 41 | $(".fragment").addClass("card card-body bg-gray"); 42 | $(".memitem").addClass("card"); 43 | $(".memproto").addClass("card-header"); 44 | $(".memdoc").addClass("card-body"); 45 | $("span.mlabel").addClass("badge bg-info"); 46 | 47 | $("table.memberdecls").addClass("table"); 48 | $("[class^=memitem]").addClass("active"); 49 | 50 | $("div.ah").addClass("btn btn-secondary"); 51 | $("span.mlabels").addClass("pull-right"); 52 | $("table.mlabels").css("width", "100%") 53 | $("td.mlabels-right").addClass("pull-right"); 54 | 55 | $("div.ttc").addClass("card card-info"); 56 | $("div.ttname").addClass("card-header"); 57 | $("div.ttdef,div.ttdoc,div.ttdeci").addClass("card-body"); 58 | 59 | $('div.tabs').addClass('container card card-body bg-gray mb-3'); 60 | $('div.tabs2').addClass('container card card-body bg-gray mb-3'); 61 | $('div.tabs3').addClass('container card card-body bg-gray mb-3'); 62 | $('div.header').addClass('container'); 63 | $('div.contents').addClass('container'); 64 | $('div.groupHeader').addClass('alert-link').parent().parent().addClass('alert alert-info'); 65 | 66 | $('#MSearchBox').remove();//.parent().appendTo('#topmenu'); 67 | 68 | $('code').each(function() { $(this).html($(this).html().replace("–", "--")); } ); 69 | }); 70 | -------------------------------------------------------------------------------- /docs/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meetecho/imquic/6c00455baba179ea04a428d2b5c6dc353aef77eb/docs/favicon.ico -------------------------------------------------------------------------------- /docs/footer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 12 | 13 | 14 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /docs/header-api.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | $title 10 | 11 | 12 | 13 | $treeview 14 | $search 15 | $mathjax 16 | $extrastylesheet 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 47 | -------------------------------------------------------------------------------- /docs/header-internal.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | $title 10 | 11 | 12 | 13 | $treeview 14 | $search 15 | $mathjax 16 | $extrastylesheet 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 47 | -------------------------------------------------------------------------------- /docs/header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | $title 10 | 11 | 12 | 13 | $treeview 14 | $search 15 | $mathjax 16 | $extrastylesheet 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 47 | -------------------------------------------------------------------------------- /docs/imquic.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 80px; 3 | } 4 | 5 | a { 6 | text-decoration: none; 7 | } 8 | 9 | .hide { 10 | display: none !important; 11 | } 12 | 13 | .z-2 { 14 | z-index: 2000; 15 | } 16 | 17 | .rounded { 18 | border-radius: 5px; 19 | } 20 | 21 | .centered { 22 | display: block; 23 | margin: auto; 24 | } 25 | 26 | .relative { 27 | position: relative; 28 | } 29 | 30 | .top-left { 31 | position: absolute; 32 | top: 0px; 33 | left: 0px; 34 | } 35 | 36 | .top-right { 37 | position: absolute; 38 | top: 0px; 39 | right: 0px; 40 | } 41 | 42 | .bottom-left { 43 | position: absolute; 44 | bottom: 0px; 45 | left: 0px; 46 | } 47 | 48 | .bottom-right { 49 | position: absolute; 50 | bottom: 0px; 51 | right: 0px; 52 | } 53 | 54 | .navbar-brand { 55 | margin-left: 0px !important; 56 | } 57 | 58 | .navbar { 59 | --bs-navbar-padding-y: 0.5rem; 60 | -webkit-box-shadow: 0px 3px 5px rgba(100, 100, 100, 0.49); 61 | -moz-box-shadow: 0px 3px 5px rgba(100, 100, 100, 0.49); 62 | box-shadow: 0px 3px 5px rgba(100, 100, 100, 0.49); 63 | } 64 | 65 | .navbar-header { 66 | padding-left: 40px; 67 | } 68 | 69 | .btn-group-xs > .btn, .btn-xs { 70 | padding: 1px 5px; 71 | font-size: 12px; 72 | line-height: 1.5; 73 | border-radius: 3px; 74 | } 75 | 76 | .divider { 77 | width: 100%; 78 | text-align: center; 79 | } 80 | 81 | .divider hr { 82 | margin-left: auto; 83 | margin-right: auto; 84 | width: 45%; 85 | } 86 | 87 | .fa-2 { 88 | font-size: 2em !important; 89 | } 90 | .fa-3 { 91 | font-size: 4em !important; 92 | } 93 | .fa-4 { 94 | font-size: 7em !important; 95 | } 96 | .fa-xl { 97 | font-size: 12em !important; 98 | } 99 | .fa-6 { 100 | font-size: 20em !important; 101 | } 102 | 103 | div.no-video-container { 104 | position: relative; 105 | } 106 | 107 | .no-video-icon { 108 | width: 100%; 109 | height: 240px; 110 | text-align: center; 111 | padding-top: 5rem !important; 112 | } 113 | 114 | .no-video-text { 115 | text-align: center; 116 | position: absolute; 117 | bottom: 0px; 118 | right: 0px; 119 | left: 0px; 120 | font-size: 24px; 121 | } 122 | 123 | .meetecho-logo { 124 | padding: 12px !important; 125 | } 126 | 127 | .meetecho-logo > img { 128 | height: 26px; 129 | } 130 | 131 | pre { 132 | white-space: pre-wrap; 133 | white-space: -moz-pre-wrap; 134 | white-space: -pre-wrap; 135 | white-space: -o-pre-wrap; 136 | word-wrap: break-word; 137 | background-color: #f5f5f5; 138 | } 139 | 140 | .bg-gray { 141 | background-color: #f5f5f5; 142 | } 143 | -------------------------------------------------------------------------------- /docs/mainpage-api.dox: -------------------------------------------------------------------------------- 1 | /*! 2 | * \mainpage imquic (public documentation) 3 | * 4 | * \par Public Documentation for the imquic QUIC library 5 | * This is the main public API documentation for the imquic QUIC library, generated with the help of 6 | * Doxygen. 7 | * 8 | * It contains information about the functions and structures that imquic 9 | * exposes in order to be used in third party applications (e.g., our 10 | * own QUIC/WebTransport/RoQ/MoQ examples). If you're interested in 11 | * exploring the internals of the library, e.g., for the purpose of 12 | * modifying the behavour of the library itself or contribute changes 13 | * you deem relevant, please refer to the internal documentation 14 | * instead. 15 | * 16 | * To learn about how to initialize and use imquic, you can refer to the 17 | * \ref publicapi documentation, which will also introduce you to how 18 | * you can set up generic QUIC endpoint, configure callbacks to be notified 19 | * about relevant events, and interacting with connections. 20 | * 21 | * Should you be interested in the native support that imquic has for 22 | * some protocols, after studying the API basics you can refer to the 23 | * dedicated APIs the library provides for those protocols instead. At 24 | * the time of writing, imquic has \ref moqapi and \ref roqapi. 25 | */ 26 | 27 | /** \defgroup API imquic API 28 | * @{ 29 | * @} 30 | */ 31 | 32 | /** \defgroup MoQ Media Over QUIC (MoQ) 33 | * @{ 34 | * @} 35 | */ 36 | 37 | /** \defgroup RoQ RTP Over QUIC (RoQ) 38 | * @{ 39 | * @} 40 | */ 41 | -------------------------------------------------------------------------------- /docs/mainpage.dox: -------------------------------------------------------------------------------- 1 | /*! 2 | * \mainpage imquic - QUIC library 3 | * 4 | * \par Developer Documentation for the imquic QUIC library 5 | * This is the main developer documentation for the imquic QUIC library, generated with the help of 6 | * Doxygen. Make sure you 7 | * check the \ref deps before attempting a compilation. If you are 8 | * interested in how to compile, install and use imquic, 9 | * checkout the \ref readme information. A \ref faq page is also available, 10 | * as well as an overview on \ref changelog. 11 | * 12 | * \par An open source QUIC library tailored for media 13 | * imquic was specifically written to study, explore and prototype the 14 | * delivery of real-time media on top of QUIC, as a potential alternative, 15 | * or complement, to what WebRTC provides (e.g., via the 16 | * Janus WebRTC Server). 17 | * As such, while it can in theory be used for generic QUIC applications 18 | * as well, it has been mostly used to test new media protocols like 19 | * RTP Over QUIC (RoQ) and Media Over QUIC (MoQ). Different blog posts 20 | * are available that provide more context, like this 21 | * QUIC intro, 22 | * a RoQ devblog 23 | * and one on MoQ. 24 | * The imquic repo also comes with a few \ref examples that 25 | * demonstrate the usage of all those protocols in different roles and 26 | * scenarios. 27 | * 28 | * While you can write your own RoQ and MoQ parsers on top of the raw 29 | * imquic QUIC functionality, the library also provides an internal 30 | * implementation of both protocols, with ad-hoc interfaces aimed at 31 | * making their use in an application as simple as possible. 32 | * 33 | * \par Public (API) and internal documentation 34 | * The documentation is divided in two parts: 35 | * 36 | * - a public documentation, in case you're interested 37 | * in using imquic in your application; 38 | * - an internal documentation, in case you're interested 39 | * in studying imquic and/oc contributing changes and fixes. 40 | * 41 | * \section copyright Copyright and author 42 | * 43 | * imquic © 2024-2025 Meetecho (https://www.meetecho.com/) 44 | * 45 | * \author Lorenzo Miniero ( \ref credits ) 46 | * 47 | * \section lcns License 48 | * This program is free software, distributed under the terms of the MIT 49 | * License. For more details and information, see the \ref license page. 50 | * 51 | */ 52 | 53 | /*! \page readme README 54 | * \verbinclude README.md 55 | */ 56 | 57 | /*! \page deps Dependencies 58 | * 59 | * The library and the examples depend on the following open source 60 | * software and libraries, so make sure you install the related development 61 | * versions before attempting a compilation: 62 | * 63 | * - \b GLib: http://library.gnome.org/devel/glib/ 64 | * - \b pkg-config: http://www.freedesktop.org/wiki/Software/pkg-config/ 65 | * - \b quictls: https://quictls.github.io/ (QUIC TLS) 66 | * 67 | */ 68 | 69 | /*! \page examples Examples 70 | * \verbinclude examples/README.md 71 | */ 72 | 73 | /*! \page debug Debugging imquic 74 | * 75 | * In the magical world of fairies and unicorns, the sun always shines 76 | * and everything always works smoothly and without issues. Unfortunately, 77 | * this is not the world we live in, and so you might still encounter 78 | * issues using imquic, e.g., unexpected crashes and the like. We always 79 | * try and tackle bugs as soon as we spot them, but some issues may be 80 | * always lingering in the background. 81 | * 82 | * Should you encounter a bug or a crash, open a new 83 | * issue 84 | * on GitHub. Make sure you carefully read the 85 | * guidelines 86 | * for contributing, or otherwise we may decide to close the issue and 87 | * not even look at it. 88 | * 89 | * What's important for us to look into issues and bugs definitely is 90 | * having enough information to do so. As such, whenever possible try to 91 | * provide as many details and data as possible. Quite useful to us are 92 | * GDB stacktraces and/or AddressSanitizer output. The following sections 93 | * give a quick overview on how you can collect this information after 94 | * a crash, but for a more detailed description of the tools you should 95 | * refer to the related documentation pages and tutorials. 96 | * 97 | * \section gdb GDB 98 | * GDB is the GNU Project Debugger 99 | * and is an effective tool for looking at what has happened (or is 100 | * happening) inside an application. As such, it's quite useful to spot 101 | * bugs and the like, as it can provide information about the values of 102 | * variables when they were used and the application crashed. 103 | * 104 | * First of all make sure that debugging symbols are enabled by reconfiguring imquic like this: 105 | * 106 | \verbatim 107 | CFLAGS="-Og -g3 -ggdb3 -fno-omit-frame-pointer" ./configure [..] 108 | \endverbatim 109 | * 110 | * Once done configuring, do a \c make \c clean (to make sure 111 | * everything is recompiled from scratch) and then a \c make and \c make \c install. 112 | * 113 | * When imquic crashes, you should get a core dump file somewhere. This is 114 | * a recorded state of the application memory at the time of crashing, and 115 | * so a backtrace of what lead to an issue can help. You can open such 116 | * a core dump file via gdb this way: 117 | * 118 | \verbatim 119 | gdb /path/to/bin/your-imquic-app /path/to/coredump 120 | gdb bt 121 | \endverbatim 122 | * 123 | * The \c bt command retrieves the backtrace, and is what you should provide 124 | * as part of your new issue. 125 | * 126 | * \note Please \c DON'T paste this backtrace in the issue text. Use a 127 | * service like Gist or 128 | * Pastebin and pass the generated 129 | * link instead. 130 | * 131 | * \section sanitizer Address Sanitizer 132 | * An even better tool for spotting issues is 133 | * Address Sanitizer, 134 | * a fast memory error detector. Since it can spot memory errors, it's 135 | * very useful to find out about hidden race conditions and the like. 136 | * 137 | * Unlike GDB which can be used as is, though, to use Address Sanitizer 138 | * you'll first need to recompile imquic with some new settings, as it 139 | * requires a specific dependency on a library, libasan, which you'll need 140 | * to install through your repository manager if needed. Besides, you'll 141 | * need at least gcc 4.8 for this to work: older versions of gcc won't 142 | * work. 143 | * 144 | * Once you've installed libasan, reconfigure imquic like this: 145 | * 146 | \verbatim 147 | CFLAGS="-O0 -g3 -ggdb3 -fno-omit-frame-pointer -fno-optimize-sibling-calls -fsanitize=address -fsanitize-address-use-after-scope -fno-sanitize-recover=all" LDFLAGS="-fsanitize=address" ./configure [..] 148 | \endverbatim 149 | * 150 | * Of course you're free to add whatever additional configuration parameter 151 | * you were using before: the important parts are the environment variables 152 | * before that. Once done configuring, do a \c make \c clean (to make sure 153 | * everything is recompiled from scratch) and then a \c make and \c make \c install 154 | * as usual. 155 | * 156 | * At this point, your imquic version should be Address Sanitizer compliant. 157 | * To make sure, try using \c ldd to check whether libasan is indeed a 158 | * dependency or not: 159 | * 160 | \verbatim 161 | ldd src/.libs/libimquic.so | grep asan 162 | \endverbatim 163 | * 164 | * If it is, you're done: whenever imquic crashes for any reason, you'll 165 | * get additional output from Address Sanitizer automatically with details 166 | * on what went wrong, and that's what you should provide as part of the 167 | * issue content. Just as a side note, please beware that using Address 168 | * Sanitizer imquic will run just a bit slower, even though not to the 169 | * point of being unusable (as when using, e.g., valgrind). 170 | * 171 | * \note Please \c DON'T paste Address Sanitizer output in the issue text. 172 | * Use a service like Gist or 173 | * Pastebin and pass the generated 174 | * link instead. 175 | * 176 | */ 177 | 178 | /*! \page credits Credits 179 | * 180 | * imquic © 2024-2025 Meetecho (https://www.meetecho.com/) 181 | * 182 | * \b Author: 183 | * Lorenzo Miniero 184 | * 185 | * Several open source components have been used to implement this software: 186 | * 187 | * - \b GLib: http://library.gnome.org/devel/glib/ 188 | * - \b pkg-config: http://www.freedesktop.org/wiki/Software/pkg-config/ 189 | * - \b quictls: https://quictls.github.io/ (QUIC TLS) 190 | * 191 | */ 192 | 193 | /*! \page license License 194 | * 195 | * This program is free software, distributed under the terms of the MIT License. 196 | * 197 | * \verbinclude LICENSE 198 | */ 199 | 200 | /*! \page changelog Tagged versions and Changelog 201 | * 202 | * There are different tagged versions on the imquic repository. We plan to 203 | * tag a new version any time a breaking change and/or a set of comprehensive 204 | * changes and fixes is going to be merged/applied to imquic, and so the 205 | * Changelog below can act as a simple and quick summary of which changes 206 | * are available in each version. 207 | * 208 | * \verbinclude CHANGELOG.md 209 | */ 210 | 211 | /*! \page faq Frequently Asked Questions 212 | * 213 | * TBD. 214 | */ 215 | -------------------------------------------------------------------------------- /docs/meetecho-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/meetecho/imquic/6c00455baba179ea04a428d2b5c6dc353aef77eb/docs/meetecho-logo.png -------------------------------------------------------------------------------- /examples/Makefile.am: -------------------------------------------------------------------------------- 1 | bin_PROGRAMS = 2 | 3 | if ENABLE_ECHO_EXAMPLES 4 | 5 | bin_PROGRAMS += imquic-echo-server imquic-echo-client 6 | 7 | imquic_echo_server_SOURCES = \ 8 | echo-server.c \ 9 | echo-server-options.c \ 10 | echo-server-options.h 11 | imquic_echo_server_CFLAGS = \ 12 | $(AM_CFLAGS) \ 13 | $(IMQUIC_CFLAGS) \ 14 | -I ../src \ 15 | $(NULL) 16 | imquic_echo_server_LDADD = \ 17 | $(IMQUIC_LIBS) \ 18 | $(IMQUIC_MANUAL_LIBS) \ 19 | -L../src/.libs -limquic \ 20 | $(NULL) 21 | 22 | imquic_echo_client_SOURCES = \ 23 | echo-client.c \ 24 | echo-client-options.c \ 25 | echo-client-options.h 26 | imquic_echo_client_CFLAGS = \ 27 | $(AM_CFLAGS) \ 28 | $(IMQUIC_CFLAGS) \ 29 | -I ../src \ 30 | $(NULL) 31 | imquic_echo_client_LDADD = \ 32 | $(IMQUIC_LIBS) \ 33 | $(IMQUIC_MANUAL_LIBS) \ 34 | -L../src/.libs -limquic \ 35 | $(NULL) 36 | 37 | endif 38 | 39 | if ENABLE_MOQ_EXAMPLES 40 | 41 | bin_PROGRAMS += imquic-moq-relay imquic-moq-pub imquic-moq-sub imquic-moq-test 42 | 43 | imquic_moq_relay_SOURCES = \ 44 | moq-relay.c \ 45 | moq-relay-options.c \ 46 | moq-relay-options.h \ 47 | moq-utils.c \ 48 | moq-utils.h 49 | imquic_moq_relay_CFLAGS = \ 50 | $(AM_CFLAGS) \ 51 | $(IMQUIC_CFLAGS) \ 52 | -I ../src \ 53 | $(NULL) 54 | imquic_moq_relay_LDADD = \ 55 | $(IMQUIC_LIBS) \ 56 | $(IMQUIC_MANUAL_LIBS) \ 57 | -L../src/.libs -limquic \ 58 | $(NULL) 59 | 60 | imquic_moq_pub_SOURCES = \ 61 | moq-pub.c \ 62 | moq-pub-options.c \ 63 | moq-pub-options.h \ 64 | moq-utils.c \ 65 | moq-utils.h 66 | imquic_moq_pub_CFLAGS = \ 67 | $(AM_CFLAGS) \ 68 | $(IMQUIC_CFLAGS) \ 69 | -I ../src \ 70 | $(NULL) 71 | imquic_moq_pub_LDADD = \ 72 | $(IMQUIC_LIBS) \ 73 | $(IMQUIC_MANUAL_LIBS) \ 74 | -L../src/.libs -limquic \ 75 | $(NULL) 76 | 77 | imquic_moq_sub_SOURCES = \ 78 | moq-sub.c \ 79 | moq-sub-options.c \ 80 | moq-sub-options.h \ 81 | moq-utils.c \ 82 | moq-utils.h 83 | imquic_moq_sub_CFLAGS = \ 84 | $(AM_CFLAGS) \ 85 | $(IMQUIC_CFLAGS) \ 86 | -I ../src \ 87 | $(NULL) 88 | imquic_moq_sub_LDADD = \ 89 | $(IMQUIC_LIBS) \ 90 | $(IMQUIC_MANUAL_LIBS) \ 91 | -L../src/.libs -limquic \ 92 | $(NULL) 93 | 94 | imquic_moq_test_SOURCES = \ 95 | moq-test.c \ 96 | moq-test-options.c \ 97 | moq-test-options.h \ 98 | moq-utils.c \ 99 | moq-utils.h 100 | imquic_moq_test_CFLAGS = \ 101 | $(AM_CFLAGS) \ 102 | $(IMQUIC_CFLAGS) \ 103 | -I ../src \ 104 | $(NULL) 105 | imquic_moq_test_LDADD = \ 106 | $(IMQUIC_LIBS) \ 107 | $(IMQUIC_MANUAL_LIBS) \ 108 | -L../src/.libs -limquic \ 109 | $(NULL) 110 | 111 | endif 112 | 113 | if ENABLE_ROQ_EXAMPLES 114 | 115 | bin_PROGRAMS += imquic-roq-server imquic-roq-client 116 | 117 | imquic_roq_server_SOURCES = \ 118 | roq-server.c \ 119 | roq-server-options.c \ 120 | roq-server-options.h 121 | imquic_roq_server_CFLAGS = \ 122 | $(AM_CFLAGS) \ 123 | $(IMQUIC_CFLAGS) \ 124 | -I ../src \ 125 | $(NULL) 126 | imquic_roq_server_LDADD = \ 127 | $(IMQUIC_LIBS) \ 128 | $(IMQUIC_MANUAL_LIBS) \ 129 | -L../src/.libs -limquic \ 130 | $(NULL) 131 | 132 | imquic_roq_client_SOURCES = \ 133 | roq-client.c \ 134 | roq-client-options.c \ 135 | roq-client-options.h 136 | imquic_roq_client_CFLAGS = \ 137 | $(AM_CFLAGS) \ 138 | $(IMQUIC_CFLAGS) \ 139 | -I ../src \ 140 | $(NULL) 141 | imquic_roq_client_LDADD = \ 142 | $(IMQUIC_LIBS) \ 143 | $(IMQUIC_MANUAL_LIBS) \ 144 | -L../src/.libs -limquic \ 145 | $(NULL) 146 | 147 | endif 148 | 149 | .PHONY: FORCE 150 | FORCE: 151 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | imquic examples 2 | =============== 3 | 4 | This folder contains a set of examples aimed at showcasing how you can integrate imquic in applications for different use cases. Notice that they're meant as simple demo examples, and are not strictly speaking representative of the coding style you should adhere to, or indicative of the expected performance of the library. 5 | 6 | There are a few different client/server examples showing how you can use imquic for: 7 | 8 | * generic QUIC applications (raw QUIC and/or WebTransport); 9 | * native RTP over QUIC (RoQ) support; 10 | * native Media over QUIC (MoQ) support. 11 | 12 | You can choose which examples to build by passing arguments to the `./configure` script. 13 | 14 | All demos allow you to create QLOG files, assuming QLOG support is available in the library. You can enable it passing `-Q `, where `` will need to be the path to a valid folder for server examples, and a path to a file to create for client examples. By default, this will serialize QLOG to contained JSON files, but you can create sequential JSON files by passing `-J` instead. Notice that, even when QLOG is enabled, the examples won't save anything by default: you're required to specify what you want to trace using one or multiple calls to `-l ` (where `` can be `quic`, `http3`, `roq` or `moq`; `-l quic -l moq` will trace both QUIC and MoQ in the MoQ examples, for instance). 15 | 16 | ## Echo examples 17 | 18 | To build the client/server echo examples, pass `--enable-echo-examples` to the `./configure` script. This will build two command line applications, namely: 19 | 20 | * `imquic-echo-server`, a basic QUIC/WebTransport echo server; 21 | * `imquic-echo-client`, a basic QUIC/WebTransport echo client. 22 | 23 | Both provide a few configuration options: pass `-h` or `--help` for more information. 24 | 25 | This example launches a raw QUIC server (since `-q` is passed), negotiating the `doq` ALPN, listening on port `9000`, using the provided certificate, and saving the shared TLS secrets to the provided `SSLKEYLOGFILE` (e.g., for live debugging of the QUIC traffic via Wireshark): 26 | 27 | ./examples/imquic-echo-server -q -a doq -p 9000 -c ../localhost.crt -k ../localhost.key -s ../key_log.log 28 | 29 | This has the echo client connect to that server: 30 | 31 | ./examples/imquic-echo-client -q -a doq -r 127.0.0.1 -R 9000 32 | 33 | This other example launches a WebTransport server (since `-w` is passed) instead: 34 | 35 | ./examples/imquic-echo-server -w -p 9000 -c ../localhost.crt -k ../localhost.key -s ../key_log.log 36 | 37 | This has the echo client connect to that server: 38 | 39 | ./examples/imquic-echo-client -w -r 127.0.0.1 -R 9000 40 | 41 | In both cases, the client will then send a single `ciao` buffer on a bidirectional `STREAM` that the server will echo back. Both client and server can be configured to offer both raw QUIC and WebTransport at the same time. 42 | 43 | ## RTP Over QUIC (RoQ) examples 44 | 45 | To build the RTP Over QUIC (RoQ) examples, pass `--enable-roq-examples` to the `./configure` script. This will build two command line applications, namely: 46 | 47 | * `imquic-roq-server`, a basic RoQ server, that will just print the flow ID and RTP headers of the incoming packets; 48 | * `imquic-roq-client`, a basic RoQ client, that will listen for RTP packets on some UDP ports, and restream them via QUIC. 49 | 50 | Both provide a few configuration options: pass `-h` or `--help` for more information. 51 | 52 | This launches the RoQ server on port `9000`, using the provided certificate, and saving the shared TLS secrets to the provided `SSLKEYLOGFILE` (e.g., for live debugging of the QUIC traffic via Wireshark); raw QUIC is used (`-q`), but unlike before the ALPN is omitted, since it will automatically be set by the RoQ stack in the library: 53 | 54 | ./examples/imquic-roq-server -q -p 9000 -c ../localhost.crt -k ../localhost.key -s ../key_log.log 55 | 56 | This launches the RoQ client to connect to that server, waiting for audio RTP packets on port `15002` (whose flow ID on RoQ will be `0`) and for video RTP packets on port `15004` (whose flow ID on RoQ will be `1`), and using a separate `STREAM` for each RTP packet: 57 | 58 | ./examples/imquic-roq-client -q -a 15002 -A 0 -v 15004 -V 1 -r 127.0.0.1 -R 9000 -m streams 59 | 60 | Sending RTP traffic to those ports (e.g., from GStreamer, FFmpeg, Janus RTP forwarders or others), will have the RoQ client send them to the RoQ server. 61 | 62 | ## Media Over QUIC (MoQ) examples 63 | 64 | To build the Media Over QUIC (MoQ) examples, pass `--enable-moq-examples` to the `./configure` script. This will build a few command line applications, namely: 65 | 66 | * `imquic-moq-pob`, a basic MoQ publisher (basically a clone on `moq-clock` in [moq-rs](https://github.com/kixelated/moq-rs)); 67 | * `imquic-moq-sub`, a basic MoQ subscriber (with support for a few different kinds of media); 68 | * `imquic-moq-test`, a basic MoQ publisher/subscriber that implements the [testing protocol](https://afrind.github.io/moq-test/draft-afrind-moq-test.html) draft (still WIP); 69 | * `imquic-moq-relay`, a basic MoQ relay. 70 | 71 | All provide a few configuration options: pass `-h` or `--help` for more information. 72 | 73 | Having a relay available is a prerequisite for testing the client demos. The `imquic-moq-relay` application is a basic (and not very performant) relay implementation with support for most of the MoQ features. This launches a MoQ relay that can be reached both via raw QUIC and WebTransport (`-q -w`), and that only accepts connections negotiating version -07 of the draft (`-M`): 74 | 75 | ./examples/imquic-moq-relay -p 9000 -c ../localhost.crt -k ../localhost.key -q -w -M 7 -s ../key_log.log 76 | 77 | Assuming a relay (`imquic-moq-relay` or others) is listening on that address, this creates a MoQ publisher using WebTransport (`-w`) that publishes the current time to the `clock` namespace and `now` track; since `-M` is not provided, support for multiple versions of MoQ is offered: 78 | 79 | ./examples/imquic-moq-pub -r 127.0.0.1 -R 9000 -w -n clock -N now -w -s ../key_log.log 80 | 81 | A MoQ subscriber for that namespace/track (with `-t text` to tell the application to interpret the objects as text) using raw QUIC (`-q`) can be run as following: 82 | 83 | ./examples/imquic-moq-sub -r 127.0.0.1 -R 9000 -q -n clock -N now -t text -s ../key_log.log 84 | 85 | Assuming [moq-rs](https://github.com/kixelated/moq-rs)'s `moq-pub` application is publishing a video file to a relay, this command will subscribe to the MP4 container video (`-t mp4` will instruct the subscriber to save the objects to the provided file): 86 | 87 | ./examples/imquic-moq-sub -r 127.0.0.1 -R 9000 -w -n pippo -N 0.mp4 -t mp4 -o test.mp4 88 | 89 | Assuming Meta's [moxygen](https://github.com/facebookexperimental/moxygen) relay is running on that address and that a [moq-encoder-player](https://github.com/facebookexperimental/moq-encoder-player/) instance is publishing audio and video, this creates a MoQ subscriber (on WebTransport) to both tracks that prints the LOC header (`-t loc`) of each incoming object: 90 | 91 | ./examples/imquic-moq-sub -r 127.0.0.1 -R 4433 -w -H /moq -n vc -N 12345678-audio -N 12345678-video -a secret -t loc -w -s ../key_log.log 92 | 93 | `imquic-moq-sub` also supports `FETCH` to obtain objects from a relay, both in standalone and (assuming v08 of the draft is used) joining mode. You enable `FETCH` by specifying the order you want using `-f`: by default this enables standalone fetch, but if you want a joining one (meaning a `SUBSCRIBE` is sent too) you also need to specify the preceding group offset via the `-j` property. This is an example of subscribing to the current time with a joining fetch that's just interested in all objects from the latest group (`-j 0`): 94 | 95 | ./examples/imquic-moq-sub -r 127.0.0.1 -R 9000 -w -n clock -N now -t text -M 8 -f ascending -j 0 96 | 97 | `imquic-moq-test` implements the publisher side of the [MoQT tester draft](https://afrind.github.io/moq-test/). At the time of writing, even though it's a publisher, it acts as a QUIC and/or WebTransport server and never announces any namespace, meaning it's expected that subscribers interested in testing it will need to connect to it directly, as part of point-to-point functional tests. This is an example of how it can be launched as a server that supports both raw QUIC and WebTransport (since both `-q` and `-w` are passed): 98 | 99 | ./examples/imquic-moq-test -c ../localhost.crt -k ../localhost.key -p 9000 -q -w 100 | 101 | You can then use the demo subscriber to specify which objects should be delivered and how by using the namespace tuple as specified in the draft, e.g.: 102 | 103 | ./examples/imquic-moq-sub -r 127.0.0.1 -R 9000 -w -n moq-test-00 -n 0 -n 2 -n 1 -n 10 -n "" -n 6 -n 10 -n 2 -n 500 -n 2 -n 2 -n 1 -n 3 -n 3 -N "" 104 | -------------------------------------------------------------------------------- /examples/echo-client-options.c: -------------------------------------------------------------------------------- 1 | /* 2 | * imquic 3 | * 4 | * Author: Lorenzo Miniero 5 | * License: MIT 6 | * 7 | * Command line options for imquic-echo-client 8 | * 9 | */ 10 | 11 | #include "echo-client-options.h" 12 | 13 | static GOptionContext *opts = NULL; 14 | 15 | gboolean demo_options_parse(demo_options *options, int argc, char *argv[]) { 16 | /* Supported command-line arguments */ 17 | GOptionEntry opt_entries[] = { 18 | { "text", 't', 0, G_OPTION_ARG_STRING, &options->text, "Text to send (default=ciao)", "string" }, 19 | { "bind", 'b', 0, G_OPTION_ARG_STRING, &options->ip, "Local IP address to bind to (default=all interfaces)", "IP" }, 20 | { "port", 'p', 0, G_OPTION_ARG_INT, &options->port, "Local port to bind to (default=0, random)", "port" }, 21 | { "remote-host", 'r', 0, G_OPTION_ARG_STRING, &options->remote_host, "QUIC server to connect to (default=none)", "IP" }, 22 | { "remote-port", 'R', 0, G_OPTION_ARG_INT, &options->remote_port, "Port of the QUIC server (default=none)", "port" }, 23 | { "cert-pem", 'c', 0, G_OPTION_ARG_STRING, &options->cert_pem, "Certificate to use (default=none)", "path" }, 24 | { "cert-key", 'k', 0, G_OPTION_ARG_STRING, &options->cert_key, "Certificate key to use (default=none)", "path" }, 25 | { "cert-pwd", 'P', 0, G_OPTION_ARG_STRING, &options->cert_pwd, "Certificate password to use (default=none)", "string" }, 26 | { "secrets-log", 's', 0, G_OPTION_ARG_STRING, &options->secrets_log, "Save the exchanged secrets to a file compatible with Wireshark (default=none)", "path" }, 27 | { "sni", 'S', 0, G_OPTION_ARG_STRING, &options->sni, "SNI to use (default=localhost)", "sni" }, 28 | { "raw-quic", 'q', 0, G_OPTION_ARG_NONE, &options->raw_quic, "Whether raw QUIC should be offered for the connection or not (default=no)", NULL }, 29 | { "alpn", 'a', 0, G_OPTION_ARG_STRING, &options->alpn, "ALPN to negotiate, if using raw QUIC (default=none)", "alpn" }, 30 | { "webtransport", 'w', 0, G_OPTION_ARG_NONE, &options->webtransport, "Whether WebTransport should be offered for the connection or not (default=no)", NULL }, 31 | { "zero-rtt", '0', 0, G_OPTION_ARG_STRING, &options->ticket_file, "Whether early data via 0-RTT should be supported, and what file to use for writing/reading the session ticket (default=none)", "path" }, 32 | { "path", 'H', 0, G_OPTION_ARG_STRING, &options->path, "In case WebTransport is used, path to use for the HTTP/3 request (default=/)", "HTTP/3 path" }, 33 | { "qlog-path", 'Q', 0, G_OPTION_ARG_STRING, &options->qlog_path, "Save a QLOG file for this connection (default=none)", "path" }, 34 | { "qlog-logging", 'l', 0, G_OPTION_ARG_STRING_ARRAY, &options->qlog_logging, "Save these events to QLOG (can be called multiple times to save multiple things; default=none)", "quic|http3" }, 35 | { "qlog-sequential", 'J', 0, G_OPTION_ARG_NONE, &options->qlog_sequential, "Whether sequential JSON should be used for the QLOG file, instead of regular JSON (default=no)", NULL }, 36 | { "debug-level", 'd', 0, G_OPTION_ARG_INT, &options->debug_level, "Debug/logging level (0=disable debugging, 7=maximum debug level; default=4)", "1-7" }, 37 | { "debug-locks", 'L', 0, G_OPTION_ARG_NONE, &options->debug_locks, "Whether to verbosely debug mutex/lock accesses (default=no)", NULL }, 38 | { "debug-refcounts", 'C', 0, G_OPTION_ARG_NONE, &options->debug_refcounts, "Whether to verbosely debug reference counting (default=no)", NULL }, 39 | { NULL, 0, 0, 0, NULL, NULL, NULL }, 40 | }; 41 | 42 | /* Parse the command-line arguments */ 43 | GError *error = NULL; 44 | opts = g_option_context_new(""); 45 | g_option_context_set_help_enabled(opts, TRUE); 46 | g_option_context_add_main_entries(opts, opt_entries, NULL); 47 | if(!g_option_context_parse(opts, &argc, &argv, &error)) { 48 | g_print("%s\n", error->message); 49 | g_error_free(error); 50 | demo_options_destroy(); 51 | return FALSE; 52 | } 53 | 54 | /* Done */ 55 | return TRUE; 56 | } 57 | 58 | void demo_options_show_usage(void) { 59 | if(opts == NULL) 60 | return; 61 | char *help = g_option_context_get_help(opts, TRUE, NULL); 62 | g_print("\n%s", help); 63 | g_free(help); 64 | } 65 | 66 | void demo_options_destroy(void) { 67 | if(opts != NULL) 68 | g_option_context_free(opts); 69 | opts = NULL; 70 | } 71 | -------------------------------------------------------------------------------- /examples/echo-client-options.h: -------------------------------------------------------------------------------- 1 | /* 2 | * imquic 3 | * 4 | * Author: Lorenzo Miniero 5 | * License: MIT 6 | * 7 | * Command line options for imquic-echo-client 8 | * 9 | */ 10 | 11 | #ifndef ECHO_CLIENT_OPTIONS 12 | #define ECHO_CLIENT_OPTIONS 13 | 14 | #include 15 | 16 | /*! \brief Struct containing the parsed command line options */ 17 | typedef struct demo_options { 18 | const char *text; 19 | const char *ip; 20 | int port; 21 | const char *remote_host; 22 | int remote_port; 23 | const char *cert_pem; 24 | const char *cert_key; 25 | const char *cert_pwd; 26 | const char *ticket_file; 27 | const char *secrets_log; 28 | const char *sni; 29 | gboolean raw_quic; 30 | const char *alpn; 31 | gboolean webtransport; 32 | const char *path; 33 | const char *qlog_path; 34 | const char **qlog_logging; 35 | gboolean qlog_sequential; 36 | int debug_level; 37 | gboolean debug_locks; 38 | gboolean debug_refcounts; 39 | } demo_options; 40 | 41 | /* Helper method to parse the command line options */ 42 | gboolean demo_options_parse(demo_options *opts, int argc, char *argv[]); 43 | 44 | /* Helper method to show the application usage */ 45 | void demo_options_show_usage(void); 46 | 47 | /*! Helper method to get rid of the options parser resources */ 48 | void demo_options_destroy(void); 49 | 50 | #endif 51 | -------------------------------------------------------------------------------- /examples/echo-client.c: -------------------------------------------------------------------------------- 1 | /* 2 | * imquic 3 | * 4 | * Author: Lorenzo Miniero 5 | * License: MIT 6 | * 7 | * Basic QUIC/WebTransport echo-client 8 | * 9 | */ 10 | 11 | #include 12 | 13 | #include "echo-client-options.h" 14 | 15 | /* Command line options */ 16 | static demo_options options = { 0 }; 17 | 18 | /* Our connection */ 19 | static imquic_connection *echo_conn = NULL; 20 | 21 | /* Signal */ 22 | static volatile int stop = 0; 23 | static void imquic_demo_handle_signal(int signum) { 24 | switch(g_atomic_int_get(&stop)) { 25 | case 0: 26 | IMQUIC_PRINT("Stopping server, please wait...\n"); 27 | break; 28 | case 1: 29 | IMQUIC_PRINT("In a hurry? I'm trying to free resources cleanly, here!\n"); 30 | break; 31 | default: 32 | IMQUIC_PRINT("Ok, leaving immediately...\n"); 33 | break; 34 | } 35 | g_atomic_int_inc(&stop); 36 | if(g_atomic_int_get(&stop) > 2) 37 | exit(1); 38 | } 39 | 40 | static void imquic_demo_new_connection(imquic_connection *conn, void *user_data) { 41 | /* Got new connection */ 42 | imquic_connection_ref(conn); 43 | echo_conn = conn; 44 | IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] New connection\n", imquic_get_connection_name(conn)); 45 | /* Send our text */ 46 | const char *text = options.text ? options.text : "ciao"; 47 | uint64_t stream_id = 0; 48 | imquic_new_stream_id(conn, TRUE, &stream_id); 49 | imquic_send_on_stream(conn, stream_id, (uint8_t *)text, 0, strlen(text), TRUE); 50 | } 51 | 52 | static void imquic_demo_stream_incoming(imquic_connection *conn, uint64_t stream_id, 53 | uint8_t *bytes, uint64_t offset, uint64_t length, gboolean complete) { 54 | /* Got incoming data via STREAM */ 55 | IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] [STREAM-%"SCNu64"] Got data: %"SCNu64"--%"SCNu64" (%s)\n", 56 | imquic_get_connection_name(conn), 57 | stream_id, offset, offset+length, (complete ? "complete" : "not complete")); 58 | if(length > 0) { 59 | int len = length; 60 | IMQUIC_LOG(IMQUIC_LOG_INFO, " -- %.*s\n", len, (char *)(bytes)); 61 | for(size_t i=0; i 65535) { 117 | IMQUIC_LOG(IMQUIC_LOG_FATAL, "Invalid local port\n"); 118 | ret = 1; 119 | goto done; 120 | } 121 | if(!options.raw_quic && !options.webtransport) { 122 | IMQUIC_LOG(IMQUIC_LOG_FATAL, "No raw QUIC or WebTransport enabled (enable at least one)\n"); 123 | ret = 1; 124 | goto done; 125 | } 126 | if(options.raw_quic) { 127 | if(options.alpn == NULL || strlen(options.alpn) == 0) { 128 | IMQUIC_LOG(IMQUIC_LOG_FATAL, "Missing ALPN to negotiate\n"); 129 | ret = 1; 130 | goto done; 131 | } 132 | IMQUIC_LOG(IMQUIC_LOG_INFO, "ALPN: %s\n", options.alpn); 133 | } 134 | if(options.ticket_file != NULL) 135 | IMQUIC_LOG(IMQUIC_LOG_INFO, "Early data support enabled (ticket file '%s')\n", options.ticket_file); 136 | 137 | /* Check if we need to create a QLOG file, and which we should save */ 138 | gboolean qlog_quic = FALSE, qlog_http3 = FALSE; 139 | if(options.qlog_path != NULL) { 140 | IMQUIC_LOG(IMQUIC_LOG_INFO, "Creating QLOG file(s) in '%s'\n", options.qlog_path); 141 | if(options.qlog_sequential) 142 | IMQUIC_LOG(IMQUIC_LOG_INFO, " -- Using sequential JSON\n"); 143 | int i = 0; 144 | while(options.qlog_logging != NULL && options.qlog_logging[i] != NULL) { 145 | if(!strcasecmp(options.qlog_logging[i], "quic")) { 146 | IMQUIC_LOG(IMQUIC_LOG_INFO, " -- Logging QUIC events\n"); 147 | qlog_quic = TRUE; 148 | } else if(!strcasecmp(options.qlog_logging[i], "http3") && options.webtransport) { 149 | IMQUIC_LOG(IMQUIC_LOG_INFO, " -- Logging HTTP/3 events\n"); 150 | qlog_http3 = TRUE; 151 | } 152 | i++; 153 | } 154 | } 155 | IMQUIC_LOG(IMQUIC_LOG_INFO, "\n"); 156 | 157 | /* Initialize the library and create a server */ 158 | if(imquic_init(options.secrets_log) < 0) { 159 | ret = 1; 160 | goto done; 161 | } 162 | imquic_client *client = imquic_create_client("echo-client", 163 | IMQUIC_CONFIG_INIT, 164 | IMQUIC_CONFIG_TLS_CERT, options.cert_pem, 165 | IMQUIC_CONFIG_TLS_KEY, options.cert_key, 166 | IMQUIC_CONFIG_TLS_PASSWORD, options.cert_pwd, 167 | IMQUIC_CONFIG_LOCAL_BIND, options.ip, 168 | IMQUIC_CONFIG_LOCAL_PORT, options.port, 169 | IMQUIC_CONFIG_REMOTE_HOST, options.remote_host, 170 | IMQUIC_CONFIG_REMOTE_PORT, options.remote_port, 171 | IMQUIC_CONFIG_SNI, options.sni, 172 | IMQUIC_CONFIG_RAW_QUIC, options.raw_quic, 173 | IMQUIC_CONFIG_ALPN, options.alpn, 174 | IMQUIC_CONFIG_WEBTRANSPORT, options.webtransport, 175 | IMQUIC_CONFIG_EARLY_DATA, (options.ticket_file != NULL), 176 | IMQUIC_CONFIG_TICKET_FILE, options.ticket_file, 177 | IMQUIC_CONFIG_HTTP3_PATH, options.path, 178 | IMQUIC_CONFIG_QLOG_PATH, options.qlog_path, 179 | IMQUIC_CONFIG_QLOG_QUIC, qlog_quic, 180 | IMQUIC_CONFIG_QLOG_HTTP3, qlog_http3, 181 | IMQUIC_CONFIG_QLOG_SEQUENTIAL, options.qlog_sequential, 182 | IMQUIC_CONFIG_DONE, NULL); 183 | if(client == NULL) { 184 | ret = 1; 185 | goto done; 186 | } 187 | imquic_set_new_connection_cb(client, imquic_demo_new_connection); 188 | imquic_set_stream_incoming_cb(client, imquic_demo_stream_incoming); 189 | imquic_set_datagram_incoming_cb(client, imquic_demo_datagram_incoming); 190 | imquic_set_connection_gone_cb(client, imquic_demo_connection_gone); 191 | imquic_start_endpoint(client); 192 | 193 | while(!stop) 194 | g_usleep(100000); 195 | 196 | imquic_shutdown_endpoint(client); 197 | 198 | done: 199 | imquic_deinit(); 200 | if(ret == 1) 201 | demo_options_show_usage(); 202 | demo_options_destroy(); 203 | 204 | /* Done */ 205 | IMQUIC_PRINT("Bye!\n"); 206 | exit(ret); 207 | } 208 | -------------------------------------------------------------------------------- /examples/echo-server-options.c: -------------------------------------------------------------------------------- 1 | /* 2 | * imquic 3 | * 4 | * Author: Lorenzo Miniero 5 | * License: MIT 6 | * 7 | * Command line options for imquic-echo-server 8 | * 9 | */ 10 | 11 | #include "echo-server-options.h" 12 | 13 | static GOptionContext *opts = NULL; 14 | 15 | gboolean demo_options_parse(demo_options *options, int argc, char *argv[]) { 16 | /* Supported command-line arguments */ 17 | GOptionEntry opt_entries[] = { 18 | { "bind", 'b', 0, G_OPTION_ARG_STRING, &options->ip, "Local IP address to bind to (default=all interfaces)", "IP" }, 19 | { "port", 'p', 0, G_OPTION_ARG_INT, &options->port, "Local port to bind to (default=0, random)", "port" }, 20 | { "cert-pem", 'c', 0, G_OPTION_ARG_STRING, &options->cert_pem, "Certificate to use (default=none)", "path" }, 21 | { "cert-key", 'k', 0, G_OPTION_ARG_STRING, &options->cert_key, "Certificate key to use (default=none)", "path" }, 22 | { "cert-pwd", 'P', 0, G_OPTION_ARG_STRING, &options->cert_pwd, "Certificate password to use (default=none)", "string" }, 23 | { "secrets-log", 's', 0, G_OPTION_ARG_STRING, &options->secrets_log, "Save the exchanged secrets to a file compatible with Wireshark (default=none)", "path" }, 24 | { "raw-quic", 'q', 0, G_OPTION_ARG_NONE, &options->raw_quic, "Whether raw QUIC should be offered for connections or not (default=no)", NULL }, 25 | { "alpn", 'a', 0, G_OPTION_ARG_STRING, &options->alpn, "ALPN to negotiate, if using raw QUIC (default=none)", "alpn" }, 26 | { "webtransport", 'w', 0, G_OPTION_ARG_NONE, &options->webtransport, "Whether WebTransport should be offered for connections or not (default=no)", NULL }, 27 | { "zero-rtt", '0', 0, G_OPTION_ARG_NONE, &options->early_data, "Whether early data via 0-RTT should be supported (default=no)", NULL }, 28 | { "qlog-path", 'Q', 0, G_OPTION_ARG_STRING, &options->qlog_path, "Path to a folder where to save QLOG files for all connections (default=none)", "path" }, 29 | { "qlog-logging", 'l', 0, G_OPTION_ARG_STRING_ARRAY, &options->qlog_logging, "Save these events to QLOG (can be called multiple times to save multiple things; default=none)", "quic|http3" }, 30 | { "qlog-sequential", 'J', 0, G_OPTION_ARG_NONE, &options->qlog_sequential, "Whether sequential JSON should be used for the QLOG file, instead of regular JSON (default=no)", NULL }, 31 | { "debug-level", 'd', 0, G_OPTION_ARG_INT, &options->debug_level, "Debug/logging level (0=disable debugging, 7=maximum debug level; default=4)", "1-7" }, 32 | { "debug-locks", 'L', 0, G_OPTION_ARG_NONE, &options->debug_locks, "Whether to verbosely debug mutex/lock accesses (default=no)", NULL }, 33 | { "debug-refcounts", 'C', 0, G_OPTION_ARG_NONE, &options->debug_refcounts, "Whether to verbosely debug reference counting (default=no)", NULL }, 34 | { NULL, 0, 0, 0, NULL, NULL, NULL }, 35 | }; 36 | 37 | /* Parse the command-line arguments */ 38 | GError *error = NULL; 39 | opts = g_option_context_new(""); 40 | g_option_context_set_help_enabled(opts, TRUE); 41 | g_option_context_add_main_entries(opts, opt_entries, NULL); 42 | if(!g_option_context_parse(opts, &argc, &argv, &error)) { 43 | g_print("%s\n", error->message); 44 | g_error_free(error); 45 | demo_options_destroy(); 46 | return FALSE; 47 | } 48 | 49 | /* Done */ 50 | return TRUE; 51 | } 52 | 53 | void demo_options_show_usage(void) { 54 | if(opts == NULL) 55 | return; 56 | char *help = g_option_context_get_help(opts, TRUE, NULL); 57 | g_print("\n%s", help); 58 | g_free(help); 59 | } 60 | 61 | void demo_options_destroy(void) { 62 | if(opts != NULL) 63 | g_option_context_free(opts); 64 | opts = NULL; 65 | } 66 | -------------------------------------------------------------------------------- /examples/echo-server-options.h: -------------------------------------------------------------------------------- 1 | /* 2 | * imquic 3 | * 4 | * Author: Lorenzo Miniero 5 | * License: MIT 6 | * 7 | * Command line options for imquic-echo-server 8 | * 9 | */ 10 | 11 | #ifndef ECHO_SERVER_OPTIONS 12 | #define ECHO_SERVER_OPTIONS 13 | 14 | #include 15 | 16 | /*! \brief Struct containing the parsed command line options */ 17 | typedef struct demo_options { 18 | const char *ip; 19 | int port; 20 | const char *cert_pem; 21 | const char *cert_key; 22 | const char *cert_pwd; 23 | const char *secrets_log; 24 | gboolean raw_quic; 25 | const char *alpn; 26 | gboolean webtransport; 27 | gboolean early_data; 28 | const char *qlog_path; 29 | const char **qlog_logging; 30 | gboolean qlog_sequential; 31 | int debug_level; 32 | gboolean debug_locks; 33 | gboolean debug_refcounts; 34 | } demo_options; 35 | 36 | /* Helper method to parse the command line options */ 37 | gboolean demo_options_parse(demo_options *opts, int argc, char *argv[]); 38 | 39 | /* Helper method to show the application usage */ 40 | void demo_options_show_usage(void); 41 | 42 | /*! Helper method to get rid of the options parser resources */ 43 | void demo_options_destroy(void); 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /examples/echo-server.c: -------------------------------------------------------------------------------- 1 | /* 2 | * imquic 3 | * 4 | * Author: Lorenzo Miniero 5 | * License: MIT 6 | * 7 | * Basic QUIC/WebTransport echo-server 8 | * 9 | */ 10 | 11 | #include 12 | 13 | #include "echo-server-options.h" 14 | 15 | /* Command line options */ 16 | static demo_options options = { 0 }; 17 | 18 | /* Signal */ 19 | static volatile int stop = 0; 20 | static void imquic_demo_handle_signal(int signum) { 21 | switch(g_atomic_int_get(&stop)) { 22 | case 0: 23 | IMQUIC_PRINT("Stopping server, please wait...\n"); 24 | break; 25 | case 1: 26 | IMQUIC_PRINT("In a hurry? I'm trying to free resources cleanly, here!\n"); 27 | break; 28 | default: 29 | IMQUIC_PRINT("Ok, leaving immediately...\n"); 30 | break; 31 | } 32 | g_atomic_int_inc(&stop); 33 | if(g_atomic_int_get(&stop) > 2) 34 | exit(1); 35 | } 36 | 37 | /* Handled connections */ 38 | static GHashTable *connections = NULL; 39 | 40 | static void imquic_demo_new_connection(imquic_connection *conn, void *user_data) { 41 | /* Got new connection */ 42 | imquic_connection_ref(conn); 43 | g_hash_table_insert(connections, conn, conn); 44 | IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] New connection\n", imquic_get_connection_name(conn)); 45 | } 46 | 47 | static void imquic_demo_stream_incoming(imquic_connection *conn, uint64_t stream_id, 48 | uint8_t *bytes, uint64_t offset, uint64_t length, gboolean complete) { 49 | /* Got incoming data via STREAM */ 50 | IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] [STREAM-%"SCNu64"] Got data: %"SCNu64"--%"SCNu64" (%s)\n", 51 | imquic_get_connection_name(conn), 52 | stream_id, offset, offset+length, (complete ? "complete" : "not complete")); 53 | if(length > 0) { 54 | int len = length; 55 | IMQUIC_LOG(IMQUIC_LOG_INFO, " -- %.*s\n", len, (char *)(bytes)); 56 | } 57 | /* FIXME Send it back */ 58 | imquic_send_on_stream(conn, stream_id, bytes, offset, length, complete); 59 | } 60 | 61 | static void imquic_demo_datagram_incoming(imquic_connection *conn, uint8_t *bytes, uint64_t length) { 62 | /* Got incoming data via DATAGRAM */ 63 | IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] [DATAGRAM] Got data: %"SCNu64"\n", imquic_get_connection_name(conn), length); 64 | int len = length; 65 | IMQUIC_LOG(IMQUIC_LOG_INFO, " -- %.*s\n", len - 1, (char *)(bytes + 1)); 66 | /* FIXME Send it back */ 67 | imquic_send_on_datagram(conn, bytes, length); 68 | } 69 | 70 | static void imquic_demo_connection_gone(imquic_connection *conn) { 71 | /* Connection was closed */ 72 | IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] Connection gone\n", imquic_get_connection_name(conn)); 73 | if(g_hash_table_remove(connections, conn)) 74 | imquic_connection_unref(conn); 75 | } 76 | 77 | int main(int argc, char *argv[]) { 78 | /* Handle SIGINT (CTRL-C), SIGTERM (from service managers) */ 79 | signal(SIGINT, imquic_demo_handle_signal); 80 | signal(SIGTERM, imquic_demo_handle_signal); 81 | 82 | IMQUIC_PRINT("imquic version %s\n", imquic_get_version_string_full()); 83 | IMQUIC_PRINT(" -- %s (commit hash)\n", imquic_get_build_sha()); 84 | IMQUIC_PRINT(" -- %s (build time)\n\n", imquic_get_build_time()); 85 | 86 | /* Initialize some command line options defaults */ 87 | options.debug_level = IMQUIC_LOG_INFO; 88 | /* Let's call our cmdline parser */ 89 | if(!demo_options_parse(&options, argc, argv)) { 90 | demo_options_show_usage(); 91 | demo_options_destroy(); 92 | exit(1); 93 | } 94 | /* Logging level */ 95 | imquic_set_log_level(options.debug_level); 96 | /* Debugging */ 97 | if(options.debug_locks) 98 | imquic_set_lock_debugging(TRUE); 99 | if(options.debug_refcounts) 100 | imquic_set_refcount_debugging(TRUE); 101 | 102 | int ret = 0; 103 | if(options.cert_pem == NULL || strlen(options.cert_pem) == 0 || options.cert_key == NULL || strlen(options.cert_key) == 0) { 104 | IMQUIC_LOG(IMQUIC_LOG_FATAL, "Missing certificate/key\n"); 105 | ret = 1; 106 | goto done; 107 | } 108 | if(options.port > 65535) { 109 | IMQUIC_LOG(IMQUIC_LOG_FATAL, "Invalid port\n"); 110 | ret = 1; 111 | goto done; 112 | } 113 | if(!options.raw_quic && !options.webtransport) { 114 | IMQUIC_LOG(IMQUIC_LOG_FATAL, "No raw QUIC or WebTransport enabled (enable at least one)\n"); 115 | ret = 1; 116 | goto done; 117 | } 118 | if(options.raw_quic) { 119 | if(options.alpn == NULL || strlen(options.alpn) == 0) { 120 | IMQUIC_LOG(IMQUIC_LOG_FATAL, "Missing ALPN to negotiate\n"); 121 | ret = 1; 122 | goto done; 123 | } 124 | IMQUIC_LOG(IMQUIC_LOG_INFO, "ALPN: %s\n", options.alpn); 125 | } 126 | if(options.early_data) 127 | IMQUIC_LOG(IMQUIC_LOG_INFO, "Early data support enabled\n"); 128 | 129 | /* Check if we need to create a QLOG file, and which we should save */ 130 | gboolean qlog_quic = FALSE, qlog_http3 = FALSE; 131 | if(options.qlog_path != NULL) { 132 | IMQUIC_LOG(IMQUIC_LOG_INFO, "Creating QLOG file(s) in '%s'\n", options.qlog_path); 133 | if(options.qlog_sequential) 134 | IMQUIC_LOG(IMQUIC_LOG_INFO, " -- Using sequential JSON\n"); 135 | int i = 0; 136 | while(options.qlog_logging != NULL && options.qlog_logging[i] != NULL) { 137 | if(!strcasecmp(options.qlog_logging[i], "quic")) { 138 | IMQUIC_LOG(IMQUIC_LOG_INFO, " -- Logging QUIC events\n"); 139 | qlog_quic = TRUE; 140 | } else if(!strcasecmp(options.qlog_logging[i], "http3") && options.webtransport) { 141 | IMQUIC_LOG(IMQUIC_LOG_INFO, " -- Logging HTTP/3 events\n"); 142 | qlog_http3 = TRUE; 143 | } 144 | i++; 145 | } 146 | } 147 | IMQUIC_LOG(IMQUIC_LOG_INFO, "\n"); 148 | 149 | /* Initialize the library and create a server */ 150 | if(imquic_init(options.secrets_log) < 0) { 151 | ret = 1; 152 | goto done; 153 | } 154 | imquic_server *server = imquic_create_server("echo-server", 155 | IMQUIC_CONFIG_INIT, 156 | IMQUIC_CONFIG_TLS_CERT, options.cert_pem, 157 | IMQUIC_CONFIG_TLS_KEY, options.cert_key, 158 | IMQUIC_CONFIG_TLS_PASSWORD, options.cert_pwd, 159 | IMQUIC_CONFIG_LOCAL_BIND, options.ip, 160 | IMQUIC_CONFIG_LOCAL_PORT, options.port, 161 | IMQUIC_CONFIG_RAW_QUIC, options.raw_quic, 162 | IMQUIC_CONFIG_ALPN, options.alpn, 163 | IMQUIC_CONFIG_WEBTRANSPORT, options.webtransport, 164 | IMQUIC_CONFIG_EARLY_DATA, options.early_data, 165 | IMQUIC_CONFIG_QLOG_PATH, options.qlog_path, 166 | IMQUIC_CONFIG_QLOG_QUIC, qlog_quic, 167 | IMQUIC_CONFIG_QLOG_HTTP3, qlog_http3, 168 | IMQUIC_CONFIG_QLOG_SEQUENTIAL, options.qlog_sequential, 169 | IMQUIC_CONFIG_DONE, NULL); 170 | if(server == NULL) { 171 | ret = 1; 172 | goto done; 173 | } 174 | imquic_set_new_connection_cb(server, imquic_demo_new_connection); 175 | imquic_set_stream_incoming_cb(server, imquic_demo_stream_incoming); 176 | imquic_set_datagram_incoming_cb(server, imquic_demo_datagram_incoming); 177 | imquic_set_connection_gone_cb(server, imquic_demo_connection_gone); 178 | connections = g_hash_table_new(NULL, NULL); 179 | imquic_start_endpoint(server); 180 | 181 | while(!stop) 182 | g_usleep(100000); 183 | 184 | imquic_shutdown_endpoint(server); 185 | 186 | done: 187 | imquic_deinit(); 188 | g_hash_table_unref(connections); 189 | if(ret == 1) 190 | demo_options_show_usage(); 191 | demo_options_destroy(); 192 | 193 | /* Done */ 194 | IMQUIC_PRINT("Bye!\n"); 195 | exit(ret); 196 | } 197 | -------------------------------------------------------------------------------- /examples/moq-pub-options.c: -------------------------------------------------------------------------------- 1 | /* 2 | * imquic 3 | * 4 | * Author: Lorenzo Miniero 5 | * License: MIT 6 | * 7 | * Command line options for imquic-moq-pub 8 | * 9 | */ 10 | 11 | #include "moq-pub-options.h" 12 | 13 | static GOptionContext *opts = NULL; 14 | 15 | gboolean demo_options_parse(demo_options *options, int argc, char *argv[]) { 16 | /* Supported command-line arguments */ 17 | GOptionEntry opt_entries[] = { 18 | { "moq-draft-version", 'M', 0, G_OPTION_ARG_STRING, &options->moq_version, "MoQ draft version number to negotiate (default=any)", "|any|legacy" }, 19 | { "track-namespace", 'n', 0, G_OPTION_ARG_STRING_ARRAY, &options->track_namespace, "MoQ track namespace to publish (can be called multiple times to create a tuple; default=none)", "namespace" }, 20 | { "track-name", 'N', 0, G_OPTION_ARG_STRING, &options->track_name, "MoQ track name to publish (default=none)", "name" }, 21 | { "first-group", 'f', 0, G_OPTION_ARG_INT64, &options->first_group, "First group ID to send (default=0; sends 'Prior Group ID Gap' extension for the first sent object in that group, if set))", "id" }, 22 | { "auth-info", 'a', 0, G_OPTION_ARG_STRING, &options->auth_info, "Auth info required to subscribe, if any (default=none)", "string" }, 23 | { "delivery", 'D', 0, G_OPTION_ARG_STRING, &options->delivery, "How MoQ objects should be sent (default=subgroup; supported=datagram,subgroup,track)", "type" }, 24 | { "extensions", 'x', 0, G_OPTION_ARG_NONE, &options->extensions, "Send some extensions along objects (default=no; only supported for v08 and later)", NULL }, 25 | { "bind", 'b', 0, G_OPTION_ARG_STRING, &options->ip, "Local IP address to bind to (default=all interfaces)", "IP" }, 26 | { "port", 'p', 0, G_OPTION_ARG_INT, &options->port, "Local port to bind to (default=0, random)", "port" }, 27 | { "remote-host", 'r', 0, G_OPTION_ARG_STRING, &options->remote_host, "QUIC server to connect to (default=none)", "IP" }, 28 | { "remote-port", 'R', 0, G_OPTION_ARG_INT, &options->remote_port, "Port of the QUIC server (default=none)", "port" }, 29 | { "sni", 'S', 0, G_OPTION_ARG_STRING, &options->sni, "SNI to use (default=localhost)", "sni" }, 30 | { "raw-quic", 'q', 0, G_OPTION_ARG_NONE, &options->raw_quic, "Whether raw QUIC should be offered for the MoQ connection or not (default=no)", NULL }, 31 | { "webtransport", 'w', 0, G_OPTION_ARG_NONE, &options->webtransport, "Whether WebTransport should be offered for the MoQ connection or not (default=no)", NULL }, 32 | { "path", 'H', 0, G_OPTION_ARG_STRING, &options->path, "In case WebTransport is used, path to use for the HTTP/3 request (default=/)", "HTTP/3 path" }, 33 | { "cert-pem", 'c', 0, G_OPTION_ARG_STRING, &options->cert_pem, "Certificate to use (default=none)", "path" }, 34 | { "cert-key", 'k', 0, G_OPTION_ARG_STRING, &options->cert_key, "Certificate key to use (default=none)", "path" }, 35 | { "cert-pwd", 'P', 0, G_OPTION_ARG_STRING, &options->cert_pwd, "Certificate password to use (default=none)", "string" }, 36 | { "zero-rtt", '0', 0, G_OPTION_ARG_STRING, &options->ticket_file, "Whether early data via 0-RTT should be supported, and what file to use for writing/reading the session ticket (default=none)", "path" }, 37 | { "secrets-log", 's', 0, G_OPTION_ARG_STRING, &options->secrets_log, "Save the exchanged secrets to a file compatible with Wireshark (default=none)", "path" }, 38 | { "qlog-path", 'Q', 0, G_OPTION_ARG_STRING, &options->qlog_path, "Save a QLOG file for this connection (default=none)", "path" }, 39 | { "qlog-logging", 'l', 0, G_OPTION_ARG_STRING_ARRAY, &options->qlog_logging, "Save these events to QLOG (can be called multiple times to save multiple things; default=none)", "quic|http3|moq" }, 40 | { "qlog-sequential", 'J', 0, G_OPTION_ARG_NONE, &options->qlog_sequential, "Whether sequential JSON should be used for the QLOG file, instead of regular JSON (default=no)", NULL }, 41 | { "debug-level", 'd', 0, G_OPTION_ARG_INT, &options->debug_level, "Debug/logging level (0=disable debugging, 7=maximum debug level; default=4)", "1-7" }, 42 | { "debug-locks", 'L', 0, G_OPTION_ARG_NONE, &options->debug_locks, "Whether to verbosely debug mutex/lock accesses (default=no)", NULL }, 43 | { "debug-refcounts", 'C', 0, G_OPTION_ARG_NONE, &options->debug_refcounts, "Whether to verbosely debug reference counting (default=no)", NULL }, 44 | { NULL, 0, 0, 0, NULL, NULL, NULL }, 45 | }; 46 | 47 | /* Parse the command-line arguments */ 48 | GError *error = NULL; 49 | opts = g_option_context_new(""); 50 | g_option_context_set_help_enabled(opts, TRUE); 51 | g_option_context_add_main_entries(opts, opt_entries, NULL); 52 | if(!g_option_context_parse(opts, &argc, &argv, &error)) { 53 | g_print("%s\n", error->message); 54 | g_error_free(error); 55 | demo_options_destroy(); 56 | return FALSE; 57 | } 58 | 59 | /* Done */ 60 | return TRUE; 61 | } 62 | 63 | void demo_options_show_usage(void) { 64 | if(opts == NULL) 65 | return; 66 | char *help = g_option_context_get_help(opts, TRUE, NULL); 67 | g_print("\n%s", help); 68 | g_free(help); 69 | } 70 | 71 | void demo_options_destroy(void) { 72 | if(opts != NULL) 73 | g_option_context_free(opts); 74 | opts = NULL; 75 | } 76 | -------------------------------------------------------------------------------- /examples/moq-pub-options.h: -------------------------------------------------------------------------------- 1 | /* 2 | * imquic 3 | * 4 | * Author: Lorenzo Miniero 5 | * License: MIT 6 | * 7 | * Command line options for imquic-moq-pub 8 | * 9 | */ 10 | 11 | #ifndef MOQ_PUB_OPTIONS 12 | #define MOQ_PUB_OPTIONS 13 | 14 | #include 15 | 16 | #include 17 | 18 | /*! \brief Struct containing the parsed command line options */ 19 | typedef struct demo_options { 20 | char *moq_version; 21 | const char **track_namespace; 22 | const char *track_name; 23 | uint64_t first_group; 24 | const char *auth_info; 25 | const char *delivery; 26 | gboolean extensions; 27 | const char *ip; 28 | int port; 29 | const char *remote_host; 30 | int remote_port; 31 | const char *sni; 32 | gboolean raw_quic; 33 | gboolean webtransport; 34 | const char *path; 35 | const char *cert_pem; 36 | const char *cert_key; 37 | const char *cert_pwd; 38 | const char *ticket_file; 39 | const char *secrets_log; 40 | const char *qlog_path; 41 | const char **qlog_logging; 42 | gboolean qlog_sequential; 43 | int debug_level; 44 | gboolean debug_locks; 45 | gboolean debug_refcounts; 46 | } demo_options; 47 | 48 | /* Helper method to parse the command line options */ 49 | gboolean demo_options_parse(demo_options *opts, int argc, char *argv[]); 50 | 51 | /* Helper method to show the application usage */ 52 | void demo_options_show_usage(void); 53 | 54 | /*! Helper method to get rid of the options parser resources */ 55 | void demo_options_destroy(void); 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /examples/moq-relay-options.c: -------------------------------------------------------------------------------- 1 | /* 2 | * imquic 3 | * 4 | * Author: Lorenzo Miniero 5 | * License: MIT 6 | * 7 | * Command line options for imquic-moq-relay 8 | * 9 | */ 10 | 11 | #include "moq-relay-options.h" 12 | 13 | static GOptionContext *opts = NULL; 14 | 15 | gboolean demo_options_parse(demo_options *options, int argc, char *argv[]) { 16 | /* Supported command-line arguments */ 17 | GOptionEntry opt_entries[] = { 18 | { "moq-draft-version", 'M', 0, G_OPTION_ARG_STRING, &options->moq_version, "MoQ draft version number to negotiate (default=any)", "|any|legacy" }, 19 | { "bind", 'b', 0, G_OPTION_ARG_STRING, &options->ip, "Local IP address to bind to (default=all interfaces)", "IP" }, 20 | { "port", 'p', 0, G_OPTION_ARG_INT, &options->port, "Local port to bind to (default=0, random)", "port" }, 21 | { "raw-quic", 'q', 0, G_OPTION_ARG_NONE, &options->raw_quic, "Whether raw QUIC should be offered for MoQ connections or not (default=no)", NULL }, 22 | { "webtransport", 'w', 0, G_OPTION_ARG_NONE, &options->webtransport, "Whether WebTransport should be offered for MoQ connections or not (default=no)", NULL }, 23 | { "cert-pem", 'c', 0, G_OPTION_ARG_STRING, &options->cert_pem, "Certificate to use (default=none)", "path" }, 24 | { "cert-key", 'k', 0, G_OPTION_ARG_STRING, &options->cert_key, "Certificate key to use (default=none)", "path" }, 25 | { "cert-pwd", 'P', 0, G_OPTION_ARG_STRING, &options->cert_pwd, "Certificate password to use (default=none)", "string" }, 26 | { "zero-rtt", '0', 0, G_OPTION_ARG_NONE, &options->early_data, "Whether early data via 0-RTT should be supported (default=no)", NULL }, 27 | { "secrets-log", 's', 0, G_OPTION_ARG_STRING, &options->secrets_log, "Save the exchanged secrets to a file compatible with Wireshark (default=none)", "path" }, 28 | { "qlog-path", 'Q', 0, G_OPTION_ARG_STRING, &options->qlog_path, "Path to a folder where to save QLOG files for all connections (default=none)", "path" }, 29 | { "qlog-logging", 'l', 0, G_OPTION_ARG_STRING_ARRAY, &options->qlog_logging, "Save these events to QLOG (can be called multiple times to save multiple things; default=none)", "quic|http3|moq" }, 30 | { "qlog-sequential", 'J', 0, G_OPTION_ARG_NONE, &options->qlog_sequential, "Whether sequential JSON should be used for the QLOG file, instead of regular JSON (default=no)", NULL }, 31 | { "quiet", 'Z', 0, G_OPTION_ARG_NONE, &options->quiet, "If set, don't print about incoming objects on stdout (default=no)", NULL }, 32 | { "debug-level", 'd', 0, G_OPTION_ARG_INT, &options->debug_level, "Debug/logging level (0=disable debugging, 7=maximum debug level; default=4)", "1-7" }, 33 | { "debug-locks", 'L', 0, G_OPTION_ARG_NONE, &options->debug_locks, "Whether to verbosely debug mutex/lock accesses (default=no)", NULL }, 34 | { "debug-refcounts", 'C', 0, G_OPTION_ARG_NONE, &options->debug_refcounts, "Whether to verbosely debug reference counting (default=no)", NULL }, 35 | { NULL, 0, 0, 0, NULL, NULL, NULL }, 36 | }; 37 | 38 | /* Parse the command-line arguments */ 39 | GError *error = NULL; 40 | opts = g_option_context_new(""); 41 | g_option_context_set_help_enabled(opts, TRUE); 42 | g_option_context_add_main_entries(opts, opt_entries, NULL); 43 | if(!g_option_context_parse(opts, &argc, &argv, &error)) { 44 | g_print("%s\n", error->message); 45 | g_error_free(error); 46 | demo_options_destroy(); 47 | return FALSE; 48 | } 49 | 50 | /* Done */ 51 | return TRUE; 52 | } 53 | 54 | void demo_options_show_usage(void) { 55 | if(opts == NULL) 56 | return; 57 | char *help = g_option_context_get_help(opts, TRUE, NULL); 58 | g_print("\n%s", help); 59 | g_free(help); 60 | } 61 | 62 | void demo_options_destroy(void) { 63 | if(opts != NULL) 64 | g_option_context_free(opts); 65 | opts = NULL; 66 | } 67 | -------------------------------------------------------------------------------- /examples/moq-relay-options.h: -------------------------------------------------------------------------------- 1 | /* 2 | * imquic 3 | * 4 | * Author: Lorenzo Miniero 5 | * License: MIT 6 | * 7 | * Command line options for imquic-moq-relay 8 | * 9 | */ 10 | 11 | #ifndef MOQ_RELAY_OPTIONS 12 | #define MOQ_RELAY_OPTIONS 13 | 14 | #include 15 | 16 | /*! \brief Struct containing the parsed command line options */ 17 | typedef struct demo_options { 18 | char *moq_version; 19 | const char *ip; 20 | int port; 21 | gboolean raw_quic; 22 | gboolean webtransport; 23 | const char *cert_pem; 24 | const char *cert_key; 25 | const char *cert_pwd; 26 | gboolean early_data; 27 | const char *secrets_log; 28 | const char *qlog_path; 29 | const char **qlog_logging; 30 | gboolean qlog_sequential; 31 | gboolean quiet; 32 | int debug_level; 33 | gboolean debug_locks; 34 | gboolean debug_refcounts; 35 | } demo_options; 36 | 37 | /* Helper method to parse the command line options */ 38 | gboolean demo_options_parse(demo_options *opts, int argc, char *argv[]); 39 | 40 | /* Helper method to show the application usage */ 41 | void demo_options_show_usage(void); 42 | 43 | /*! Helper method to get rid of the options parser resources */ 44 | void demo_options_destroy(void); 45 | 46 | #endif 47 | -------------------------------------------------------------------------------- /examples/moq-sub-options.c: -------------------------------------------------------------------------------- 1 | /* 2 | * imquic 3 | * 4 | * Author: Lorenzo Miniero 5 | * License: MIT 6 | * 7 | * Command line options for imquic-moq-sub 8 | * 9 | */ 10 | 11 | #include "moq-sub-options.h" 12 | 13 | static GOptionContext *opts = NULL; 14 | 15 | gboolean demo_options_parse(demo_options *options, int argc, char *argv[]) { 16 | /* Supported command-line arguments */ 17 | GOptionEntry opt_entries[] = { 18 | { "moq-draft-version", 'M', 0, G_OPTION_ARG_STRING, &options->moq_version, "MoQ draft version number to negotiate (default=any)", "|any|legacy" }, 19 | { "track-namespace", 'n', 0, G_OPTION_ARG_STRING_ARRAY, &options->track_namespace, "MoQ track namespace to subscribe to (can be called multiple times to create a tuple; default=none)", "namespace" }, 20 | { "track-name", 'N', 0, G_OPTION_ARG_STRING_ARRAY, &options->track_name, "MoQ track name to subscribe to (can be called multiple times to subscribe to multiple tracks; default=none)", "name" }, 21 | { "auth-info", 'a', 0, G_OPTION_ARG_STRING, &options->auth_info, "Auth info to subscribe, if needed (default=none)", "string" }, 22 | { "filter-type", 'F', 0, G_OPTION_ARG_STRING, &options->filter_type, "Filter type to use for SUBSCRIBE (default=LatestObject)", "LatestObject|NextGroupStart|AbsoluteStart|AbsoluteRange" }, 23 | { "fetch", 'f', 0, G_OPTION_ARG_STRING, &options->fetch, "Use FETCH instead of SUBSCRIBE, in the specified order (ascending/descending)", "order" }, 24 | { "join", 'j', 0, G_OPTION_ARG_INT, &options->join_offset, "When using FETCH, use a Joining Fetch and get the specified number of preceding groups (default=-1, no joining fetch)", "offset" }, 25 | { "start-group", 'g', 0, G_OPTION_ARG_INT64, &options->start_group, "Group to start from, for Standalone FETCH or when using specific filters in SUBSCRIBE (default=0)", "id" }, 26 | { "start-object", 'o', 0, G_OPTION_ARG_INT64, &options->start_object, "Object to start from, for Standalone FETCH or when using specific filters in SUBSCRIBE (default=0)", "id" }, 27 | { "end-group", 'G', 0, G_OPTION_ARG_INT64, &options->end_group, "Group to end at, for Standalone FETCH or when using specific filters in SUBSCRIBE (default=maximum, maximum)", "id" }, 28 | { "end-object", 'O', 0, G_OPTION_ARG_INT64, &options->end_object, "Object to end at, for Standalone FETCH or when using specific filters in SUBSCRIBE (default=maximum)", "id" }, 29 | { "update-subscribe", 'u', 0, G_OPTION_ARG_INT, &options->update_subscribe, "When using SUSBCRIBE, disable forwarding at first, and send a SUBSCRIBE_UPDATE after a few seconds (default=-1, get objects right away)", "seconds" }, 30 | { "media-type", 't', 0, G_OPTION_ARG_STRING, &options->media_type, "Kind of media to subscribe to (default=none)", "none|text|hex|loc|mp4" }, 31 | { "target-file", 'T', 0, G_OPTION_ARG_STRING, &options->target_file, "File to save MoQ object payloads to (default=none)", "path" }, 32 | { "bind", 'b', 0, G_OPTION_ARG_STRING, &options->ip, "Local IP address to bind to (default=all interfaces)", "IP" }, 33 | { "port", 'p', 0, G_OPTION_ARG_INT, &options->port, "Local port to bind to (default=0, random)", "port" }, 34 | { "remote-host", 'r', 0, G_OPTION_ARG_STRING, &options->remote_host, "QUIC server to connect to (default=none)", "IP" }, 35 | { "remote-port", 'R', 0, G_OPTION_ARG_INT, &options->remote_port, "Port of the QUIC server (default=none)", "port" }, 36 | { "sni", 'S', 0, G_OPTION_ARG_STRING, &options->sni, "SNI to use (default=localhost)", "sni" }, 37 | { "raw-quic", 'q', 0, G_OPTION_ARG_NONE, &options->raw_quic, "Whether raw QUIC should be offered for the MoQ connection or not (default=no)", NULL }, 38 | { "webtransport", 'w', 0, G_OPTION_ARG_NONE, &options->webtransport, "Whether WebTransport should be offered for the MoQ connection or not (default=no)", NULL }, 39 | { "path", 'H', 0, G_OPTION_ARG_STRING, &options->path, "In case WebTransport is used, path to use for the HTTP/3 request (default=/)", "HTTP/3 path" }, 40 | { "cert-pem", 'c', 0, G_OPTION_ARG_STRING, &options->cert_pem, "Certificate to use (default=none)", "path" }, 41 | { "cert-key", 'k', 0, G_OPTION_ARG_STRING, &options->cert_key, "Certificate key to use (default=none)", "path" }, 42 | { "cert-pwd", 'P', 0, G_OPTION_ARG_STRING, &options->cert_pwd, "Certificate password to use (default=none)", "string" }, 43 | { "zero-rtt", '0', 0, G_OPTION_ARG_STRING, &options->ticket_file, "Whether early data via 0-RTT should be supported, and what file to use for writing/reading the session ticket (default=none)", "path" }, 44 | { "secrets-log", 's', 0, G_OPTION_ARG_STRING, &options->secrets_log, "Save the exchanged secrets to a file compatible with Wireshark (default=none)", "path" }, 45 | { "qlog-path", 'Q', 0, G_OPTION_ARG_STRING, &options->qlog_path, "Save a QLOG file for this connection (default=none)", "path" }, 46 | { "qlog-logging", 'l', 0, G_OPTION_ARG_STRING_ARRAY, &options->qlog_logging, "Save these events to QLOG (can be called multiple times to save multiple things; default=none)", "quic|http3|moq" }, 47 | { "qlog-sequential", 'J', 0, G_OPTION_ARG_NONE, &options->qlog_sequential, "Whether sequential JSON should be used for the QLOG file, instead of regular JSON (default=no)", NULL }, 48 | { "debug-level", 'd', 0, G_OPTION_ARG_INT, &options->debug_level, "Debug/logging level (0=disable debugging, 7=maximum debug level; default=4)", "1-7" }, 49 | { "debug-locks", 'L', 0, G_OPTION_ARG_NONE, &options->debug_locks, "Whether to verbosely debug mutex/lock accesses (default=no)", NULL }, 50 | { "debug-refcounts", 'C', 0, G_OPTION_ARG_NONE, &options->debug_refcounts, "Whether to verbosely debug reference counting (default=no)", NULL }, 51 | { NULL, 0, 0, 0, NULL, NULL, NULL }, 52 | }; 53 | 54 | /* Parse the command-line arguments */ 55 | GError *error = NULL; 56 | opts = g_option_context_new(""); 57 | g_option_context_set_help_enabled(opts, TRUE); 58 | g_option_context_add_main_entries(opts, opt_entries, NULL); 59 | if(!g_option_context_parse(opts, &argc, &argv, &error)) { 60 | g_print("%s\n", error->message); 61 | g_error_free(error); 62 | demo_options_destroy(); 63 | return FALSE; 64 | } 65 | 66 | /* Done */ 67 | return TRUE; 68 | } 69 | 70 | void demo_options_show_usage(void) { 71 | if(opts == NULL) 72 | return; 73 | char *help = g_option_context_get_help(opts, TRUE, NULL); 74 | g_print("\n%s", help); 75 | g_free(help); 76 | } 77 | 78 | void demo_options_destroy(void) { 79 | if(opts != NULL) 80 | g_option_context_free(opts); 81 | opts = NULL; 82 | } 83 | -------------------------------------------------------------------------------- /examples/moq-sub-options.h: -------------------------------------------------------------------------------- 1 | /* 2 | * imquic 3 | * 4 | * Author: Lorenzo Miniero 5 | * License: MIT 6 | * 7 | * Command line options for imquic-moq-sub 8 | * 9 | */ 10 | 11 | #ifndef MOQ_SUB_OPTIONS 12 | #define MOQ_SUB_OPTIONS 13 | 14 | #include 15 | 16 | #include 17 | 18 | /*! \brief Struct containing the parsed command line options */ 19 | typedef struct demo_options { 20 | char *moq_version; 21 | const char **track_namespace; 22 | const char **track_name; 23 | const char *auth_info; 24 | const char *filter_type; 25 | uint64_t start_group; 26 | uint64_t start_object; 27 | uint64_t end_group; 28 | uint64_t end_object; 29 | int update_subscribe; 30 | const char *fetch; 31 | int join_offset; 32 | const char *media_type; 33 | const char *target_file; 34 | const char *ip; 35 | int port; 36 | const char *remote_host; 37 | int remote_port; 38 | const char *sni; 39 | gboolean raw_quic; 40 | gboolean webtransport; 41 | const char *path; 42 | const char *cert_pem; 43 | const char *cert_key; 44 | const char *cert_pwd; 45 | const char *ticket_file; 46 | const char *secrets_log; 47 | const char *qlog_path; 48 | const char **qlog_logging; 49 | gboolean qlog_sequential; 50 | int debug_level; 51 | gboolean debug_locks; 52 | gboolean debug_refcounts; 53 | } demo_options; 54 | 55 | /* Helper method to parse the command line options */ 56 | gboolean demo_options_parse(demo_options *opts, int argc, char *argv[]); 57 | 58 | /* Helper method to show the application usage */ 59 | void demo_options_show_usage(void); 60 | 61 | /*! Helper method to get rid of the options parser resources */ 62 | void demo_options_destroy(void); 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /examples/moq-test-options.c: -------------------------------------------------------------------------------- 1 | /* 2 | * imquic 3 | * 4 | * Author: Lorenzo Miniero 5 | * License: MIT 6 | * 7 | * Command line options for imquic-moq-test 8 | * 9 | */ 10 | 11 | #include "moq-test-options.h" 12 | 13 | static GOptionContext *opts = NULL; 14 | 15 | gboolean demo_options_parse(demo_options *options, int argc, char *argv[]) { 16 | /* Supported command-line arguments */ 17 | GOptionEntry opt_entries[] = { 18 | { "moq-draft-version", 'M', 0, G_OPTION_ARG_STRING, &options->moq_version, "MoQ draft version number to negotiate (default=any)", "|any|legacy" }, 19 | { "bind", 'b', 0, G_OPTION_ARG_STRING, &options->ip, "Local IP address to bind to (default=all interfaces)", "IP" }, 20 | { "port", 'p', 0, G_OPTION_ARG_INT, &options->port, "Local port to bind to (default=0, random)", "port" }, 21 | { "raw-quic", 'q', 0, G_OPTION_ARG_NONE, &options->raw_quic, "Whether raw QUIC should be offered for MoQ connections or not (default=no)", NULL }, 22 | { "webtransport", 'w', 0, G_OPTION_ARG_NONE, &options->webtransport, "Whether WebTransport should be offered for MoQ connections or not (default=no)", NULL }, 23 | { "cert-pem", 'c', 0, G_OPTION_ARG_STRING, &options->cert_pem, "Certificate to use (default=none)", "path" }, 24 | { "cert-key", 'k', 0, G_OPTION_ARG_STRING, &options->cert_key, "Certificate key to use (default=none)", "path" }, 25 | { "cert-pwd", 'P', 0, G_OPTION_ARG_STRING, &options->cert_pwd, "Certificate password to use (default=none)", "string" }, 26 | { "zero-rtt", '0', 0, G_OPTION_ARG_NONE, &options->early_data, "Whether early data via 0-RTT should be supported (default=no)", NULL }, 27 | { "secrets-log", 's', 0, G_OPTION_ARG_STRING, &options->secrets_log, "Save the exchanged secrets to a file compatible with Wireshark (default=none)", "path" }, 28 | { "qlog-path", 'Q', 0, G_OPTION_ARG_STRING, &options->qlog_path, "Path to a folder where to save QLOG files for all connections (default=none)", "path" }, 29 | { "qlog-logging", 'l', 0, G_OPTION_ARG_STRING_ARRAY, &options->qlog_logging, "Save these events to QLOG (can be called multiple times to save multiple things; default=none)", "quic|http3|moq" }, 30 | { "qlog-sequential", 'J', 0, G_OPTION_ARG_NONE, &options->qlog_sequential, "Whether sequential JSON should be used for the QLOG file, instead of regular JSON (default=no)", NULL }, 31 | { "debug-level", 'd', 0, G_OPTION_ARG_INT, &options->debug_level, "Debug/logging level (0=disable debugging, 7=maximum debug level; default=4)", "1-7" }, 32 | { "debug-locks", 'L', 0, G_OPTION_ARG_NONE, &options->debug_locks, "Whether to verbosely debug mutex/lock accesses (default=no)", NULL }, 33 | { "debug-refcounts", 'C', 0, G_OPTION_ARG_NONE, &options->debug_refcounts, "Whether to verbosely debug reference counting (default=no)", NULL }, 34 | { NULL, 0, 0, 0, NULL, NULL, NULL }, 35 | }; 36 | 37 | /* Parse the command-line arguments */ 38 | GError *error = NULL; 39 | opts = g_option_context_new(""); 40 | g_option_context_set_help_enabled(opts, TRUE); 41 | g_option_context_add_main_entries(opts, opt_entries, NULL); 42 | if(!g_option_context_parse(opts, &argc, &argv, &error)) { 43 | g_print("%s\n", error->message); 44 | g_error_free(error); 45 | demo_options_destroy(); 46 | return FALSE; 47 | } 48 | 49 | /* Done */ 50 | return TRUE; 51 | } 52 | 53 | void demo_options_show_usage(void) { 54 | if(opts == NULL) 55 | return; 56 | char *help = g_option_context_get_help(opts, TRUE, NULL); 57 | g_print("\n%s", help); 58 | g_free(help); 59 | } 60 | 61 | void demo_options_destroy(void) { 62 | if(opts != NULL) 63 | g_option_context_free(opts); 64 | opts = NULL; 65 | } 66 | -------------------------------------------------------------------------------- /examples/moq-test-options.h: -------------------------------------------------------------------------------- 1 | /* 2 | * imquic 3 | * 4 | * Author: Lorenzo Miniero 5 | * License: MIT 6 | * 7 | * Command line options for imquic-moq-test 8 | * 9 | */ 10 | 11 | #ifndef MOQ_TEST_OPTIONS 12 | #define MOQ_TEST_OPTIONS 13 | 14 | #include 15 | 16 | /*! \brief Struct containing the parsed command line options */ 17 | typedef struct demo_options { 18 | char *moq_version; 19 | const char *ip; 20 | int port; 21 | gboolean raw_quic; 22 | gboolean webtransport; 23 | const char *cert_pem; 24 | const char *cert_key; 25 | const char *cert_pwd; 26 | gboolean early_data; 27 | const char *secrets_log; 28 | const char *qlog_path; 29 | const char **qlog_logging; 30 | gboolean qlog_sequential; 31 | int debug_level; 32 | gboolean debug_locks; 33 | gboolean debug_refcounts; 34 | } demo_options; 35 | 36 | /* Helper method to parse the command line options */ 37 | gboolean demo_options_parse(demo_options *opts, int argc, char *argv[]); 38 | 39 | /* Helper method to show the application usage */ 40 | void demo_options_show_usage(void); 41 | 42 | /*! Helper method to get rid of the options parser resources */ 43 | void demo_options_destroy(void); 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /examples/moq-utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | * imquic 3 | * 4 | * Author: Lorenzo Miniero 5 | * License: MIT 6 | * 7 | * A few helpers and utilities that are helpful in most/all MoQ examples 8 | * 9 | */ 10 | 11 | #include "moq-utils.h" 12 | 13 | /* Helper to duplicate an object */ 14 | imquic_moq_object *imquic_moq_object_duplicate(imquic_moq_object *object) { 15 | if(object == NULL) 16 | return NULL; 17 | imquic_moq_object *new_obj = g_malloc(sizeof(imquic_moq_object)); 18 | memcpy(new_obj, object, sizeof(imquic_moq_object)); 19 | if(object->payload_len == 0) { 20 | new_obj->payload = NULL; 21 | } else { 22 | new_obj->payload = g_malloc(object->payload_len); 23 | memcpy(new_obj->payload, object->payload, object->payload_len); 24 | } 25 | if(object->extensions_len == 0) { 26 | new_obj->extensions = NULL; 27 | } else { 28 | new_obj->extensions = g_malloc(object->extensions_len); 29 | memcpy(new_obj->extensions, object->extensions, object->extensions_len); 30 | } 31 | return new_obj; 32 | } 33 | 34 | /* Helper to destroy a duplicated object */ 35 | void imquic_moq_object_cleanup(imquic_moq_object *object) { 36 | if(object) { 37 | g_free(object->payload); 38 | g_free(object->extensions); 39 | g_free(object); 40 | } 41 | } 42 | 43 | /* Helper to destroy an object extension */ 44 | void imquic_moq_object_extension_free(imquic_moq_object_extension *extension) { 45 | if(extension != NULL) { 46 | if(extension->value.data.buffer != NULL) 47 | g_free(extension->value.data.buffer); 48 | g_free(extension); 49 | } 50 | } 51 | 52 | /* Helpers to deal with auth info */ 53 | int imquic_moq_auth_info_to_bytes(imquic_connection *conn, const char *auth_info, uint8_t *auth, size_t *authlen) { 54 | if(conn == NULL || auth_info == NULL || auth == NULL || authlen == 0) 55 | return -1; 56 | if(imquic_moq_get_version(conn) < IMQUIC_MOQ_VERSION_11) { 57 | /* Just copy the string to the buffer */ 58 | *authlen = strlen(auth_info); 59 | memcpy(auth, auth_info, *authlen); 60 | } else { 61 | /* Serialize the token using the USE_VALUE alias type */ 62 | imquic_moq_auth_token token = { 0 }; 63 | token.alias_type = IMQUIC_MOQ_AUTH_TOKEN_USE_VALUE; 64 | token.token_type_set = TRUE; 65 | token.token_type = 1; /* FIXME */ 66 | token.token_value.buffer = (uint8_t *)auth_info; 67 | token.token_value.length = strlen(auth_info); 68 | size_t offset = imquic_moq_build_auth_token(&token, auth, *authlen); 69 | if(offset == 0) 70 | return -1; 71 | *authlen = offset; 72 | } 73 | return 0; 74 | } 75 | 76 | void imquic_moq_print_auth_info(imquic_connection *conn, uint8_t *auth, size_t authlen) { 77 | if(conn == NULL || auth == NULL || authlen == 0) 78 | return; 79 | if(imquic_moq_get_version(conn) < IMQUIC_MOQ_VERSION_11) { 80 | /* String */ 81 | IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] -- Authorization info: %.*s\n", 82 | imquic_get_connection_name(conn), (int)authlen, auth); 83 | } else { 84 | /* Data (hex) */ 85 | IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] -- Authorization info: ", 86 | imquic_get_connection_name(conn)); 87 | for(size_t i=0; i 5 | * License: MIT 6 | * 7 | * A few helpers and utilities that are helpful in most/all MoQ examples 8 | * 9 | */ 10 | 11 | #ifndef MOQ_UTILS 12 | #define MOQ_UTILS 13 | 14 | #include 15 | 16 | #include 17 | #include 18 | 19 | /* Helper to duplicate an object */ 20 | imquic_moq_object *imquic_moq_object_duplicate(imquic_moq_object *object); 21 | 22 | /* Helper to destroy a duplicated object */ 23 | void imquic_moq_object_cleanup(imquic_moq_object *object); 24 | 25 | /* Helper to destroy an object extension */ 26 | void imquic_moq_object_extension_free(imquic_moq_object_extension *extension); 27 | 28 | /* Helpers to deal with auth info */ 29 | int imquic_moq_auth_info_to_bytes(imquic_connection *conn, const char *auth_info, uint8_t *auth, size_t *authlen); 30 | void imquic_moq_print_auth_info(imquic_connection *conn, uint8_t *auth, size_t authlen); 31 | gboolean imquic_moq_check_auth_info(imquic_connection *conn, const char *auth_info, uint8_t *auth, size_t authlen); 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /examples/roq-client-options.c: -------------------------------------------------------------------------------- 1 | /* 2 | * imquic 3 | * 4 | * Author: Lorenzo Miniero 5 | * License: MIT 6 | * 7 | * Command line options for imquic-roq-client 8 | * 9 | */ 10 | 11 | #include "roq-client-options.h" 12 | 13 | static GOptionContext *opts = NULL; 14 | 15 | gboolean demo_options_parse(demo_options *options, int argc, char *argv[]) { 16 | /* Supported command-line arguments */ 17 | GOptionEntry opt_entries[] = { 18 | { "audio-port", 'a', 0, G_OPTION_ARG_INT, &options->audio_port, "Port to bind to for incoming audio RTP packets (default=none)", "port" }, 19 | { "audio-flow", 'A', 0, G_OPTION_ARG_INT, &options->audio_flow, "Flow ID of the audio RTP stream (default=none)", "number" }, 20 | { "video-port", 'v', 0, G_OPTION_ARG_INT, &options->video_port, "Port to bind to for incoming video RTP packets (default=none)", "port" }, 21 | { "video-flow", 'V', 0, G_OPTION_ARG_INT, &options->video_flow, "Flow ID of the video RTP stream (default=none)", "number" }, 22 | { "multiplexing", 'm', 0, G_OPTION_ARG_STRING, &options->multiplexing, "RTP multiplexing (datagram, stream or streams; default=datagram)", "mode" }, 23 | { "bind", 'b', 0, G_OPTION_ARG_STRING, &options->ip, "Local IP address to bind to (default=all interfaces)", "IP" }, 24 | { "port", 'p', 0, G_OPTION_ARG_INT, &options->port, "Local port to bind to (default=0, random)", "port" }, 25 | { "remote-host", 'r', 0, G_OPTION_ARG_STRING, &options->remote_host, "QUIC server to connect to (default=none)", "IP" }, 26 | { "remote-port", 'R', 0, G_OPTION_ARG_INT, &options->remote_port, "Port of the QUIC server (default=none)", "port" }, 27 | { "sni", 'S', 0, G_OPTION_ARG_STRING, &options->sni, "SNI to use (default=localhost)", "sni" }, 28 | { "raw-quic", 'q', 0, G_OPTION_ARG_NONE, &options->raw_quic, "Whether raw QUIC should be offered for the RoQ connection or not (default=no)", NULL }, 29 | { "webtransport", 'w', 0, G_OPTION_ARG_NONE, &options->webtransport, "Whether WebTransport should be offered for the RoQ connection or not (default=no)", NULL }, 30 | { "path", 'H', 0, G_OPTION_ARG_STRING, &options->path, "In case WebTransport is used, path to use for the HTTP/3 request (default=/)", "HTTP/3 path" }, 31 | { "cert-pem", 'c', 0, G_OPTION_ARG_STRING, &options->cert_pem, "Certificate to use (default=none)", "path" }, 32 | { "cert-key", 'k', 0, G_OPTION_ARG_STRING, &options->cert_key, "Certificate key to use (default=none)", "path" }, 33 | { "cert-pwd", 'P', 0, G_OPTION_ARG_STRING, &options->cert_pwd, "Certificate password to use (default=none)", "string" }, 34 | { "zero-rtt", '0', 0, G_OPTION_ARG_STRING, &options->ticket_file, "Whether early data via 0-RTT should be supported, and what file to use for writing/reading the session ticket (default=none)", "path" }, 35 | { "secrets-log", 's', 0, G_OPTION_ARG_STRING, &options->secrets_log, "Save the exchanged secrets to a file compatible with Wireshark (default=none)", "path" }, 36 | { "qlog-path", 'Q', 0, G_OPTION_ARG_STRING, &options->qlog_path, "Save a QLOG file for this connection (default=none)", "path" }, 37 | { "qlog-logging", 'l', 0, G_OPTION_ARG_STRING_ARRAY, &options->qlog_logging, "Save these events to QLOG (can be called multiple times to save multiple things; default=none)", "quic|http3|roq" }, 38 | { "qlog-sequential", 'J', 0, G_OPTION_ARG_NONE, &options->qlog_sequential, "Whether sequential JSON should be used for the QLOG file, instead of regular JSON (default=no)", NULL }, 39 | { "debug-level", 'd', 0, G_OPTION_ARG_INT, &options->debug_level, "Debug/logging level (0=disable debugging, 7=maximum debug level; default=4)", "1-7" }, 40 | { "debug-locks", 'L', 0, G_OPTION_ARG_NONE, &options->debug_locks, "Whether to verbosely debug mutex/lock accesses (default=no)", NULL }, 41 | { "debug-refcounts", 'C', 0, G_OPTION_ARG_NONE, &options->debug_refcounts, "Whether to verbosely debug reference counting (default=no)", NULL }, 42 | { NULL, 0, 0, 0, NULL, NULL, NULL }, 43 | }; 44 | 45 | /* Parse the command-line arguments */ 46 | GError *error = NULL; 47 | opts = g_option_context_new(""); 48 | g_option_context_set_help_enabled(opts, TRUE); 49 | g_option_context_add_main_entries(opts, opt_entries, NULL); 50 | if(!g_option_context_parse(opts, &argc, &argv, &error)) { 51 | g_print("%s\n", error->message); 52 | g_error_free(error); 53 | demo_options_destroy(); 54 | return FALSE; 55 | } 56 | 57 | /* Done */ 58 | return TRUE; 59 | } 60 | 61 | void demo_options_show_usage(void) { 62 | if(opts == NULL) 63 | return; 64 | char *help = g_option_context_get_help(opts, TRUE, NULL); 65 | g_print("\n%s", help); 66 | g_free(help); 67 | } 68 | 69 | void demo_options_destroy(void) { 70 | if(opts != NULL) 71 | g_option_context_free(opts); 72 | opts = NULL; 73 | } 74 | -------------------------------------------------------------------------------- /examples/roq-client-options.h: -------------------------------------------------------------------------------- 1 | /* 2 | * imquic 3 | * 4 | * Author: Lorenzo Miniero 5 | * License: MIT 6 | * 7 | * Command line options for imquic-roq-client 8 | * 9 | */ 10 | 11 | #ifndef ROQ_CLIENT_OPTIONS 12 | #define ROQ_CLIENT_OPTIONS 13 | 14 | #include 15 | 16 | /*! \brief Struct containing the parsed command line options */ 17 | typedef struct demo_options { 18 | int audio_flow; 19 | int audio_port; 20 | int video_flow; 21 | int video_port; 22 | const char *multiplexing; 23 | const char *ip; 24 | int port; 25 | const char *remote_host; 26 | int remote_port; 27 | const char *sni; 28 | gboolean raw_quic; 29 | gboolean webtransport; 30 | const char *path; 31 | const char *cert_pem; 32 | const char *cert_key; 33 | const char *cert_pwd; 34 | const char *ticket_file; 35 | const char *secrets_log; 36 | const char *qlog_path; 37 | const char **qlog_logging; 38 | gboolean qlog_sequential; 39 | int debug_level; 40 | gboolean debug_locks; 41 | gboolean debug_refcounts; 42 | } demo_options; 43 | 44 | /* Helper method to parse the command line options */ 45 | gboolean demo_options_parse(demo_options *opts, int argc, char *argv[]); 46 | 47 | /* Helper method to show the application usage */ 48 | void demo_options_show_usage(void); 49 | 50 | /*! Helper method to get rid of the options parser resources */ 51 | void demo_options_destroy(void); 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /examples/roq-server-options.c: -------------------------------------------------------------------------------- 1 | /* 2 | * imquic 3 | * 4 | * Author: Lorenzo Miniero 5 | * License: MIT 6 | * 7 | * Command line options for imquic-roq-server 8 | * 9 | */ 10 | 11 | #include "roq-server-options.h" 12 | 13 | static GOptionContext *opts = NULL; 14 | 15 | gboolean demo_options_parse(demo_options *options, int argc, char *argv[]) { 16 | /* Supported command-line arguments */ 17 | GOptionEntry opt_entries[] = { 18 | { "bind", 'b', 0, G_OPTION_ARG_STRING, &options->ip, "Local IP address to bind to (default=all interfaces)", "IP" }, 19 | { "port", 'p', 0, G_OPTION_ARG_INT, &options->port, "Local port to bind to (default=0, random)", "port" }, 20 | { "raw-quic", 'q', 0, G_OPTION_ARG_NONE, &options->raw_quic, "Whether raw QUIC should be offered for RoQ connections or not (default=no)", NULL }, 21 | { "webtransport", 'w', 0, G_OPTION_ARG_NONE, &options->webtransport, "Whether WebTransport should be offered for the RoQ connection or not (default=no)", NULL }, 22 | { "cert-pem", 'c', 0, G_OPTION_ARG_STRING, &options->cert_pem, "Certificate to use (default=none)", "path" }, 23 | { "cert-key", 'k', 0, G_OPTION_ARG_STRING, &options->cert_key, "Certificate key to use (default=none)", "path" }, 24 | { "cert-pwd", 'P', 0, G_OPTION_ARG_STRING, &options->cert_pwd, "Certificate password to use (default=none)", "string" }, 25 | { "zero-rtt", '0', 0, G_OPTION_ARG_NONE, &options->early_data, "Whether early data via 0-RTT should be supported (default=no)", NULL }, 26 | { "secrets-log", 's', 0, G_OPTION_ARG_STRING, &options->secrets_log, "Save the exchanged secrets to a file compatible with Wireshark (default=none)", "path" }, 27 | { "qlog-path", 'Q', 0, G_OPTION_ARG_STRING, &options->qlog_path, "Path to a folder where to save QLOG files for all connections (default=none)", "path" }, 28 | { "qlog-logging", 'l', 0, G_OPTION_ARG_STRING_ARRAY, &options->qlog_logging, "Save these events to QLOG (can be called multiple times to save multiple things; default=none)", "quic|http3|roq" }, 29 | { "qlog-sequential", 'J', 0, G_OPTION_ARG_NONE, &options->qlog_sequential, "Whether sequential JSON should be used for the QLOG file, instead of regular JSON (default=no)", NULL }, 30 | { "debug-level", 'd', 0, G_OPTION_ARG_INT, &options->debug_level, "Debug/logging level (0=disable debugging, 7=maximum debug level; default=4)", "1-7" }, 31 | { "debug-locks", 'L', 0, G_OPTION_ARG_NONE, &options->debug_locks, "Whether to verbosely debug mutex/lock accesses (default=no)", NULL }, 32 | { "debug-refcounts", 'C', 0, G_OPTION_ARG_NONE, &options->debug_refcounts, "Whether to verbosely debug reference counting (default=no)", NULL }, 33 | { NULL, 0, 0, 0, NULL, NULL, NULL }, 34 | }; 35 | 36 | /* Parse the command-line arguments */ 37 | GError *error = NULL; 38 | opts = g_option_context_new(""); 39 | g_option_context_set_help_enabled(opts, TRUE); 40 | g_option_context_add_main_entries(opts, opt_entries, NULL); 41 | if(!g_option_context_parse(opts, &argc, &argv, &error)) { 42 | g_print("%s\n", error->message); 43 | g_error_free(error); 44 | demo_options_destroy(); 45 | return FALSE; 46 | } 47 | 48 | /* Done */ 49 | return TRUE; 50 | } 51 | 52 | void demo_options_show_usage(void) { 53 | if(opts == NULL) 54 | return; 55 | char *help = g_option_context_get_help(opts, TRUE, NULL); 56 | g_print("\n%s", help); 57 | g_free(help); 58 | } 59 | 60 | void demo_options_destroy(void) { 61 | if(opts != NULL) 62 | g_option_context_free(opts); 63 | opts = NULL; 64 | } 65 | -------------------------------------------------------------------------------- /examples/roq-server-options.h: -------------------------------------------------------------------------------- 1 | /* 2 | * imquic 3 | * 4 | * Author: Lorenzo Miniero 5 | * License: MIT 6 | * 7 | * Command line options for imquic-roq-server 8 | * 9 | */ 10 | 11 | #ifndef ROQ_SERVER_OPTIONS 12 | #define ROQ_SERVER_OPTIONS 13 | 14 | #include 15 | 16 | /*! \brief Struct containing the parsed command line options */ 17 | typedef struct demo_options { 18 | const char *ip; 19 | int port; 20 | gboolean raw_quic; 21 | gboolean webtransport; 22 | const char *cert_pem; 23 | const char *cert_key; 24 | const char *cert_pwd; 25 | gboolean early_data; 26 | const char *secrets_log; 27 | const char *qlog_path; 28 | const char **qlog_logging; 29 | gboolean qlog_sequential; 30 | int debug_level; 31 | gboolean debug_locks; 32 | gboolean debug_refcounts; 33 | } demo_options; 34 | 35 | /* Helper method to parse the command line options */ 36 | gboolean demo_options_parse(demo_options *opts, int argc, char *argv[]); 37 | 38 | /* Helper method to show the application usage */ 39 | void demo_options_show_usage(void); 40 | 41 | /*! Helper method to get rid of the options parser resources */ 42 | void demo_options_destroy(void); 43 | 44 | #endif 45 | -------------------------------------------------------------------------------- /examples/roq-server.c: -------------------------------------------------------------------------------- 1 | /* 2 | * imquic 3 | * 4 | * Author: Lorenzo Miniero 5 | * License: MIT 6 | * 7 | * Basic RoQ server 8 | * 9 | */ 10 | 11 | #include 12 | 13 | #include 14 | #include 15 | 16 | #include "roq-server-options.h" 17 | 18 | /* Command line options */ 19 | static demo_options options = { 0 }; 20 | 21 | /* Signal */ 22 | static volatile int stop = 0; 23 | static void imquic_demo_handle_signal(int signum) { 24 | switch(g_atomic_int_get(&stop)) { 25 | case 0: 26 | IMQUIC_PRINT("Stopping server, please wait...\n"); 27 | break; 28 | case 1: 29 | IMQUIC_PRINT("In a hurry? I'm trying to free resources cleanly, here!\n"); 30 | break; 31 | default: 32 | IMQUIC_PRINT("Ok, leaving immediately...\n"); 33 | break; 34 | } 35 | g_atomic_int_inc(&stop); 36 | if(g_atomic_int_get(&stop) > 2) 37 | exit(1); 38 | } 39 | 40 | /* Handled connections */ 41 | static GHashTable *connections = NULL; 42 | 43 | /* RTP header */ 44 | typedef struct imquic_rtp_header { 45 | #if __BYTE_ORDER == __BIG_ENDIAN 46 | uint16_t version:2; 47 | uint16_t padding:1; 48 | uint16_t extension:1; 49 | uint16_t csrccount:4; 50 | uint16_t markerbit:1; 51 | uint16_t type:7; 52 | #elif __BYTE_ORDER == __LITTLE_ENDIAN 53 | uint16_t csrccount:4; 54 | uint16_t extension:1; 55 | uint16_t padding:1; 56 | uint16_t version:2; 57 | uint16_t type:7; 58 | uint16_t markerbit:1; 59 | #endif 60 | uint16_t seq_number; 61 | uint32_t timestamp; 62 | uint32_t ssrc; 63 | uint32_t csrc[0]; 64 | } imquic_rtp_header; 65 | static gboolean imquic_is_rtp(uint8_t *buf, guint len) { 66 | if (len < 12) 67 | return FALSE; 68 | imquic_rtp_header *header = (imquic_rtp_header *)buf; 69 | return (header->version == 2 && ((header->type < 64) || (header->type >= 96))); 70 | } 71 | 72 | /* Debugging: printing the content of a hex buffer */ 73 | static void imquic_roq_print_hex(int level, uint8_t *buf, size_t buflen) { 74 | IMQUIC_LOG(level, "\t"); 75 | for(size_t i=0; issrc), rtp->type, ntohs(rtp->seq_number), ntohl(rtp->timestamp)); 100 | } 101 | 102 | static void imquic_demo_connection_gone(imquic_connection *conn) { 103 | /* Connection was closed */ 104 | IMQUIC_LOG(IMQUIC_LOG_INFO, "[%s] RoQ connection gone\n", imquic_get_connection_name(conn)); 105 | if(g_hash_table_remove(connections, conn)) 106 | imquic_connection_unref(conn); 107 | } 108 | 109 | int main(int argc, char *argv[]) { 110 | /* Handle SIGINT (CTRL-C), SIGTERM (from service managers) */ 111 | signal(SIGINT, imquic_demo_handle_signal); 112 | signal(SIGTERM, imquic_demo_handle_signal); 113 | 114 | IMQUIC_PRINT("imquic version %s\n", imquic_get_version_string_full()); 115 | IMQUIC_PRINT(" -- %s (commit hash)\n", imquic_get_build_sha()); 116 | IMQUIC_PRINT(" -- %s (build time)\n\n", imquic_get_build_time()); 117 | 118 | /* Initialize some command line options defaults */ 119 | options.debug_level = IMQUIC_LOG_INFO; 120 | /* Let's call our cmdline parser */ 121 | if(!demo_options_parse(&options, argc, argv)) { 122 | demo_options_show_usage(); 123 | demo_options_destroy(); 124 | exit(1); 125 | } 126 | /* Logging level */ 127 | imquic_set_log_level(options.debug_level); 128 | /* Debugging */ 129 | if(options.debug_locks) 130 | imquic_set_lock_debugging(TRUE); 131 | if(options.debug_refcounts) 132 | imquic_set_refcount_debugging(TRUE); 133 | 134 | int ret = 0; 135 | if(options.cert_pem == NULL || strlen(options.cert_pem) == 0 || options.cert_key == NULL || strlen(options.cert_key) == 0) { 136 | IMQUIC_LOG(IMQUIC_LOG_FATAL, "Missing certificate/key\n"); 137 | ret = 1; 138 | goto done; 139 | } 140 | if(options.port > 65535) { 141 | IMQUIC_LOG(IMQUIC_LOG_FATAL, "Invalid port\n"); 142 | ret = 1; 143 | goto done; 144 | } 145 | if(!options.raw_quic && !options.webtransport) { 146 | IMQUIC_LOG(IMQUIC_LOG_FATAL, "No raw QUIC or WebTransport enabled (enable at least one)\n"); 147 | ret = 1; 148 | goto done; 149 | } 150 | if(options.early_data) 151 | IMQUIC_LOG(IMQUIC_LOG_INFO, "Early data support enabled\n"); 152 | 153 | /* Check if we need to create a QLOG file */ 154 | gboolean qlog_quic = FALSE, qlog_http3 = FALSE, qlog_roq = FALSE; 155 | if(options.qlog_path != NULL) { 156 | IMQUIC_LOG(IMQUIC_LOG_INFO, "Creating QLOG file(s) in '%s'\n", options.qlog_path); 157 | if(options.qlog_sequential) 158 | IMQUIC_LOG(IMQUIC_LOG_INFO, " -- Using sequential JSON\n"); 159 | IMQUIC_LOG(IMQUIC_LOG_INFO, " -- Logging QUIC events\n"); 160 | int i = 0; 161 | while(options.qlog_logging != NULL && options.qlog_logging[i] != NULL) { 162 | if(!strcasecmp(options.qlog_logging[i], "quic")) { 163 | IMQUIC_LOG(IMQUIC_LOG_INFO, " -- Logging QUIC events\n"); 164 | qlog_quic = TRUE; 165 | } else if(!strcasecmp(options.qlog_logging[i], "http3") && options.webtransport) { 166 | IMQUIC_LOG(IMQUIC_LOG_INFO, " -- Logging HTTP/3 events\n"); 167 | qlog_http3 = TRUE; 168 | } else if(!strcasecmp(options.qlog_logging[i], "roq")) { 169 | IMQUIC_LOG(IMQUIC_LOG_INFO, " -- Logging RoQ events\n"); 170 | qlog_roq = TRUE; 171 | } 172 | i++; 173 | } 174 | } 175 | IMQUIC_LOG(IMQUIC_LOG_INFO, "\n"); 176 | 177 | /* Initialize the library and create a server */ 178 | if(imquic_init(options.secrets_log) < 0) { 179 | ret = 1; 180 | goto done; 181 | } 182 | imquic_server *server = imquic_create_roq_server("roq-server", 183 | IMQUIC_CONFIG_INIT, 184 | IMQUIC_CONFIG_TLS_CERT, options.cert_pem, 185 | IMQUIC_CONFIG_TLS_KEY, options.cert_key, 186 | IMQUIC_CONFIG_TLS_PASSWORD, options.cert_pwd, 187 | IMQUIC_CONFIG_LOCAL_BIND, options.ip, 188 | IMQUIC_CONFIG_LOCAL_PORT, options.port, 189 | IMQUIC_CONFIG_RAW_QUIC, options.raw_quic, 190 | IMQUIC_CONFIG_WEBTRANSPORT, options.webtransport, 191 | IMQUIC_CONFIG_QLOG_PATH, options.qlog_path, 192 | IMQUIC_CONFIG_QLOG_QUIC, qlog_quic, 193 | IMQUIC_CONFIG_QLOG_HTTP3, qlog_http3, 194 | IMQUIC_CONFIG_QLOG_ROQ, qlog_roq, 195 | IMQUIC_CONFIG_QLOG_SEQUENTIAL, options.qlog_sequential, 196 | IMQUIC_CONFIG_EARLY_DATA, options.early_data, 197 | IMQUIC_CONFIG_DONE, NULL); 198 | if(server == NULL) { 199 | ret = 1; 200 | goto done; 201 | } 202 | if(options.raw_quic) 203 | IMQUIC_LOG(IMQUIC_LOG_INFO, "ALPN: %s\n", imquic_get_endpoint_alpn(server)); 204 | if(options.webtransport && imquic_get_endpoint_subprotocol(server) != NULL) 205 | IMQUIC_LOG(IMQUIC_LOG_INFO, "Subprotocol: %s\n", imquic_get_endpoint_subprotocol(server)); 206 | imquic_set_new_roq_connection_cb(server, imquic_demo_new_connection); 207 | imquic_set_rtp_incoming_cb(server, imquic_demo_rtp_incoming); 208 | imquic_set_roq_connection_gone_cb(server, imquic_demo_connection_gone); 209 | connections = g_hash_table_new(NULL, NULL); 210 | imquic_start_endpoint(server); 211 | 212 | while(!stop) 213 | g_usleep(100000); 214 | 215 | imquic_shutdown_endpoint(server); 216 | 217 | done: 218 | imquic_deinit(); 219 | if(connections != NULL) 220 | g_hash_table_unref(connections); 221 | if(ret == 1) 222 | demo_options_show_usage(); 223 | demo_options_destroy(); 224 | 225 | /* Done */ 226 | IMQUIC_PRINT("Bye!\n"); 227 | exit(ret); 228 | } 229 | -------------------------------------------------------------------------------- /imquic.pc.in: -------------------------------------------------------------------------------- 1 | prefix=@prefix@ 2 | exec_prefix=@exec_prefix@ 3 | libdir=@libdir@ 4 | includedir=@includedir@ 5 | 6 | Name: imquic 7 | Description: QUIC library with RTP Over QUIC (RoQ) and Media Over QUIC (MoQT) support 8 | Version: @IMQUIC_VERSION_STRING@ 9 | Requires: @IMQUIC_PACKAGES_PUBLIC@ 10 | Requires.private: @IMQUIC_PACKAGES_PRIVATE@ 11 | Libs: -L${libdir} -limquic 12 | Cflags: -I${includedir} 13 | -------------------------------------------------------------------------------- /src/Makefile.am: -------------------------------------------------------------------------------- 1 | EXTRA_DIST = $(NULL) 2 | CLEANFILES = $(NULL) 3 | 4 | headerdir = $(includedir)/imquic 5 | header_HEADERS = \ 6 | imquic/imquic.h \ 7 | imquic/debug.h \ 8 | imquic/roq.h \ 9 | imquic/moq.h 10 | 11 | lib_LTLIBRARIES = libimquic.la 12 | libimquic_la_SOURCES = \ 13 | imquic.c \ 14 | imquic/imquic.h \ 15 | internal/configuration.h \ 16 | quic.c \ 17 | internal/quic.h \ 18 | connection.c \ 19 | internal/connection.h \ 20 | stream.c \ 21 | internal/stream.h \ 22 | crypto.c \ 23 | internal/crypto.h \ 24 | error.c \ 25 | internal/error.h \ 26 | buffer.c \ 27 | internal/buffer.h \ 28 | loop.c \ 29 | internal/loop.h \ 30 | network.c \ 31 | internal/network.h \ 32 | qlog.c \ 33 | internal/qlog.h \ 34 | utils.c \ 35 | internal/utils.h \ 36 | listmap.c \ 37 | internal/listmap.h \ 38 | http3.c \ 39 | internal/http3.h \ 40 | qpack.c \ 41 | internal/qpack.h \ 42 | internal/huffman.h \ 43 | imquic-moq.c \ 44 | imquic/moq.h \ 45 | moq.c \ 46 | internal/moq.h \ 47 | imquic-roq.c \ 48 | imquic/roq.h \ 49 | roq.c \ 50 | internal/roq.h \ 51 | internal/mutex.h \ 52 | internal/refcount.h \ 53 | imquic/debug.h \ 54 | version.c \ 55 | internal/version.h 56 | libimquic_la_CFLAGS = \ 57 | $(AM_CFLAGS) \ 58 | $(IMQUIC_CFLAGS) \ 59 | $(QUICTLS_CFLAGS) \ 60 | $(BORINGSSL_CFLAGS) \ 61 | $(JANSSON_CFLAGS) \ 62 | $(NULL) 63 | libimquic_la_LDFLAGS = \ 64 | -version-info $(IMQUIC_VERSION_SO) \ 65 | $(NULL) 66 | libimquic_la_LIBADD = \ 67 | $(IMQUIC_LIBS) \ 68 | $(IMQUIC_MANUAL_LIBS) \ 69 | $(QUICTLS_LIBS) \ 70 | $(BORINGSSL_LIBS) \ 71 | $(JANSSON_LIBS) \ 72 | $(NULL) 73 | 74 | BUILT_SOURCES = version.c 75 | 76 | directory = ../.git 77 | dir_target = $(directory)-$(wildcard $(directory)) 78 | dir_present = $(directory)-$(directory) 79 | dir_absent = $(directory)- 80 | 81 | if WITH_SOURCE_DATE_EPOCH 82 | build_date = $(shell LC_ALL=C date --utc --date="@$(SOURCE_DATE_EPOCH)") 83 | else 84 | build_date = $(shell date) 85 | endif 86 | 87 | version.c: FORCE | $(dir_target) 88 | echo "$(build_date)" | awk 'BEGIN {} {print "const char *imquic_build_git_time = \""$$0"\";"} END {} ' >> version.c 89 | echo "imquic" | awk 'BEGIN {} {print "const char *imquic_name = \""$$0"\";"} END {} ' >> version.c 90 | echo "$(IMQUIC_VERSION_MAJOR)" | awk 'BEGIN {} {print "int imquic_version_major = "$$0";"} END {} ' >> version.c 91 | echo "$(IMQUIC_VERSION_MINOR)" | awk 'BEGIN {} {print "int imquic_version_minor = "$$0";"} END {} ' >> version.c 92 | echo "$(IMQUIC_VERSION_PATCH)" | awk 'BEGIN {} {print "int imquic_version_patch = "$$0";"} END {} ' >> version.c 93 | echo "$(IMQUIC_VERSION_RELEASE)" | awk 'BEGIN {} {print "const char *imquic_version_release = \""$$0"\";"} END {} ' >> version.c 94 | echo "$(IMQUIC_VERSION_STRING)" | awk 'BEGIN {} {print "const char *imquic_version_string = \""$$0"\";"} END {} ' >> version.c 95 | echo "$(IMQUIC_VERSION_STRING)/$(IMQUIC_VERSION_RELEASE)" | awk 'BEGIN {} {print "const char *imquic_version_string_full = \""$$0"\";"} END {} ' >> version.c 96 | 97 | $(dir_present): 98 | `which git` rev-parse HEAD | awk 'BEGIN {print "#include \"internal/version.h\""} {print "const char *imquic_build_git_sha = \"" $$0"\";"} END {}' > version.c 99 | 100 | $(dir_absent): 101 | echo "not-a-git-repo" | awk 'BEGIN {print "#include \"internal/version.h\""} {print "const char *imquic_build_git_sha = \"" $$0"\";"} END {}' > version.c 102 | 103 | CLEANFILES += version.c 104 | 105 | .PHONY: FORCE 106 | FORCE: 107 | -------------------------------------------------------------------------------- /src/buffer.c: -------------------------------------------------------------------------------- 1 | /*! \file buffer.c 2 | * \author Lorenzo Miniero 3 | * \copyright MIT License 4 | * \brief Buffer abstraction 5 | * \details Abstraction of a chunked buffer, to be used either during 6 | * CRYPTO exchanges in the Initial/Handshake phase, or with STREAM after 7 | * a connection has been established. It provides a high level interface 8 | * to adding chunks to the queue (in a gap-aware way), and to retrieving 9 | * data in an ordered way (waiting in case gaps are encountered). 10 | * 11 | * \ingroup Core 12 | */ 13 | 14 | #include "internal/buffer.h" 15 | #include "imquic/debug.h" 16 | 17 | imquic_buffer *imquic_buffer_create(uint64_t stream_id) { 18 | imquic_buffer *buf = g_malloc0(sizeof(imquic_buffer)); 19 | buf->stream_id = stream_id; 20 | return buf; 21 | } 22 | 23 | void imquic_buffer_destroy(imquic_buffer *buf) { 24 | if(buf == NULL) 25 | return; 26 | if(buf->chunks != NULL) 27 | g_list_free_full(buf->chunks, (GDestroyNotify)imquic_buffer_chunk_free); 28 | g_free(buf); 29 | } 30 | 31 | imquic_buffer_chunk *imquic_buffer_chunk_create(uint8_t *data, uint64_t offset, uint64_t length) { 32 | if(length == 0 || (length > 0 && data == NULL)) 33 | return NULL; 34 | imquic_buffer_chunk *chunk = g_malloc(sizeof(imquic_buffer_chunk)); 35 | chunk->data = g_malloc(length); 36 | memcpy(chunk->data, data, length); 37 | chunk->offset = offset; 38 | chunk->length = length; 39 | return chunk; 40 | } 41 | 42 | void imquic_buffer_chunk_free(imquic_buffer_chunk *chunk) { 43 | if(chunk != NULL) { 44 | g_free(chunk->data); 45 | g_free(chunk); 46 | } 47 | } 48 | 49 | /* Helpers to add or get from the buffer */ 50 | uint64_t imquic_buffer_put(imquic_buffer *buf, uint8_t *data, uint64_t offset, uint64_t length) { 51 | if(buf == NULL || length == 0 || (length > 0 && data == NULL)) 52 | return 0; 53 | if(offset < buf->base_offset) { 54 | IMQUIC_LOG(IMQUIC_LOG_WARN, "[%"SCNu64"] Ignoring already processed chunk (%"SCNu64" < %"SCNu64")\n", 55 | buf->stream_id, offset, buf->base_offset); 56 | return 0; 57 | } 58 | /* Check where we have to put it */ 59 | GList *temp = buf->chunks; 60 | if(temp == NULL) { 61 | /* Empty list, easy enough */ 62 | buf->chunks = g_list_append(buf->chunks, imquic_buffer_chunk_create(data, offset, length)); 63 | } else { 64 | /* Traverse the list and find the correct insert point */ 65 | gboolean inserted = FALSE; 66 | imquic_buffer_chunk *tc = NULL, *prev = NULL; 67 | while(temp) { 68 | tc = (imquic_buffer_chunk *)temp->data; 69 | if(tc->offset > offset) { 70 | /* Insert here */ 71 | if(offset + length > tc->offset) { 72 | IMQUIC_LOG(IMQUIC_LOG_WARN, "[%"SCNu64"] Overlapping buffer (%"SCNu64"+%"SCNu64" > %"SCNu64"), truncating chunk\n", 73 | buf->stream_id, offset, length, tc->offset); 74 | length = tc->offset - offset; 75 | } 76 | if(prev != NULL && (prev->offset + prev->length > offset)) { 77 | IMQUIC_LOG(IMQUIC_LOG_WARN, "[%"SCNu64"] Overlapping buffer (%"SCNu64"+%"SCNu64" > %"SCNu64"), truncating chunk\n", 78 | buf->stream_id, prev->offset, prev->length, offset); 79 | uint64_t diff = prev->offset + prev->length - offset; 80 | offset += diff; 81 | data += diff; 82 | length = (length >= diff ? (length - diff) : 0); 83 | } 84 | inserted = TRUE; 85 | if(length > 0) 86 | buf->chunks = g_list_insert_before(buf->chunks, temp, imquic_buffer_chunk_create(data, offset, length)); 87 | break; 88 | } 89 | prev = tc; 90 | temp = temp->next; 91 | } 92 | if(!inserted) { 93 | /* Append at the end */ 94 | if(prev != NULL && (prev->offset + prev->length > offset)) { 95 | IMQUIC_LOG(IMQUIC_LOG_WARN, "[%"SCNu64"] Overlapping buffer (%"SCNu64"+%"SCNu64" > %"SCNu64"), truncating chunk\n", 96 | buf->stream_id, prev->offset, prev->length, offset); 97 | uint64_t diff = prev->offset + prev->length - offset; 98 | offset += diff; 99 | data += diff; 100 | length = (length >= diff ? (length - diff) : 0); 101 | } 102 | if(length > 0) 103 | buf->chunks = g_list_insert_before(buf->chunks, temp, imquic_buffer_chunk_create(data, offset, length)); 104 | } 105 | } 106 | return length; 107 | } 108 | 109 | uint64_t imquic_buffer_append(imquic_buffer *buf, uint8_t *data, uint64_t length) { 110 | if(buf == NULL || length == 0 || (length > 0 && data == NULL)) 111 | return 0; 112 | GList *last = g_list_last(buf->chunks); 113 | imquic_buffer_chunk *last_chunk = (imquic_buffer_chunk *)(last ? last->data : NULL); 114 | uint64_t offset = last_chunk ? (last_chunk->offset + last_chunk->length) : buf->base_offset; 115 | /* Always appending, easy enough */ 116 | buf->chunks = g_list_append(buf->chunks, imquic_buffer_chunk_create(data, offset, length)); 117 | return 0; 118 | } 119 | 120 | imquic_buffer_chunk *imquic_buffer_peek(imquic_buffer *buf) { 121 | if(buf == NULL || buf->chunks == NULL) 122 | return NULL; 123 | imquic_buffer_chunk *chunk = (imquic_buffer_chunk *)buf->chunks->data; 124 | if(chunk->offset > buf->base_offset) { 125 | /* There's still gaps */ 126 | return NULL; 127 | } 128 | return chunk; 129 | } 130 | 131 | imquic_buffer_chunk *imquic_buffer_get(imquic_buffer *buf) { 132 | imquic_buffer_chunk *chunk = imquic_buffer_peek(buf); 133 | if(chunk != NULL) { 134 | buf->base_offset = chunk->offset + chunk->length; 135 | buf->chunks = g_list_delete_link(buf->chunks, g_list_first(buf->chunks)); 136 | } 137 | return chunk; 138 | } 139 | 140 | /* Helper to print the contents of a buffer */ 141 | void imquic_buffer_print(int level, imquic_buffer *buf) { 142 | if(buf == NULL) 143 | return; 144 | IMQUIC_LOG(level, "Buffer (base offset %"SCNu64", %d chunks)\n", 145 | buf->base_offset, g_list_length(buf->chunks)); 146 | GList *temp = buf->chunks; 147 | imquic_buffer_chunk *chunk = NULL; 148 | while(temp) { 149 | chunk = (imquic_buffer_chunk *)temp->data; 150 | IMQUIC_LOG(level, " -- %"SCNu64"-->%"SCNu64" (length %"SCNu64")\n", 151 | chunk->offset, chunk->offset + chunk->length, chunk->length); 152 | temp = temp->next; 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/error.c: -------------------------------------------------------------------------------- 1 | /*! \file error.c 2 | * \author Lorenzo Miniero 3 | * \copyright MIT License 4 | * \brief QUIC errors definitions 5 | * \details Definitions for QUIC errors, and helper functions to use them. 6 | * 7 | * \ingroup Core 8 | */ 9 | 10 | #include 11 | 12 | #include "internal/error.h" 13 | 14 | const char *imquic_error_code_str(imquic_error_code type) { 15 | switch(type) { 16 | case IMQUIC_NO_ERROR: 17 | return "NO_ERROR"; 18 | case IMQUIC_INTERNAL_ERROR: 19 | return "INTERNAL_ERROR"; 20 | case IMQUIC_CONNECTION_REFUSED: 21 | return "CONNECTION_REFUSED"; 22 | case IMQUIC_FLOW_CONTROL_ERROR: 23 | return "FLOW_CONTROL_ERROR"; 24 | case IMQUIC_STREAM_LIMIT_ERROR: 25 | return "STREAM_LIMIT_ERROR"; 26 | case IMQUIC_STREAM_STATE_ERROR: 27 | return "STREAM_STATE_ERROR"; 28 | case IMQUIC_FINAL_SIZE_ERROR: 29 | return "FINAL_SIZE_ERROR"; 30 | case IMQUIC_FRAME_ENCODING_ERROR: 31 | return "FRAME_ENCODING_ERROR"; 32 | case IMQUIC_TRANSPORT_PARAMETER_ERROR: 33 | return "TRANSPORT_PARAMETER_ERROR"; 34 | case IMQUIC_CONNECTION_ID_LIMIT_ERROR: 35 | return "CONNECTION_ID_LIMIT_ERROR"; 36 | case IMQUIC_PROTOCOL_VIOLATION: 37 | return "PROTOCOL_VIOLATION"; 38 | case IMQUIC_INVALID_TOKEN: 39 | return "INVALID_TOKEN"; 40 | case IMQUIC_APPLICATION_ERROR: 41 | return "APPLICATION_ERROR"; 42 | case IMQUIC_CRYPTO_BUFFER_EXCEEDED: 43 | return "CRYPTO_BUFFER_EXCEEDED"; 44 | case IMQUIC_KEY_UPDATE_ERROR: 45 | return "KEY_UPDATE_ERROR"; 46 | case IMQUIC_AEAD_LIMIT_REACHED: 47 | return "AEAD_LIMIT_REACHED"; 48 | case IMQUIC_NO_VIABLE_PATH: 49 | return "NO_VIABLE_PATH"; 50 | case IMQUIC_CRYPTO_ERROR: 51 | return "CRYPTO_ERROR"; 52 | default: break; 53 | } 54 | return NULL; 55 | } 56 | 57 | -------------------------------------------------------------------------------- /src/imquic/debug.h: -------------------------------------------------------------------------------- 1 | /*! \file debug.h 2 | * \author Lorenzo Miniero 3 | * \copyright MIT License 4 | * \brief Logging and Debugging 5 | * \details Implementation of a wrapper on printf (or g_print) to either log or debug. 6 | * 7 | * \ingroup API Core 8 | */ 9 | 10 | #ifndef IMQUIC_DEBUG_H 11 | #define IMQUIC_DEBUG_H 12 | 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | extern int imquic_log_level; 19 | extern gboolean imquic_log_timestamps; 20 | extern gboolean imquic_log_colors; 21 | 22 | #define IMQUIC_MAX_VARINT (((uint64_t)1 << 62) - 1) 23 | 24 | /** @name imquic log colors 25 | */ 26 | ///@{ 27 | #define IMQUIC_ANSI_COLOR_RED "\x1b[31m" 28 | #define IMQUIC_ANSI_COLOR_GREEN "\x1b[32m" 29 | #define IMQUIC_ANSI_COLOR_YELLOW "\x1b[33m" 30 | #define IMQUIC_ANSI_COLOR_BLUE "\x1b[34m" 31 | #define IMQUIC_ANSI_COLOR_MAGENTA "\x1b[35m" 32 | #define IMQUIC_ANSI_COLOR_CYAN "\x1b[36m" 33 | #define IMQUIC_ANSI_COLOR_RESET "\x1b[0m" 34 | ///@} 35 | 36 | /** @name imquic log levels 37 | */ 38 | ///@{ 39 | /*! \brief No debugging */ 40 | #define IMQUIC_LOG_NONE (0) 41 | /*! \brief Fatal error */ 42 | #define IMQUIC_LOG_FATAL (1) 43 | /*! \brief Non-fatal error */ 44 | #define IMQUIC_LOG_ERR (2) 45 | /*! \brief Warning */ 46 | #define IMQUIC_LOG_WARN (3) 47 | /*! \brief Informational message */ 48 | #define IMQUIC_LOG_INFO (4) 49 | /*! \brief Verbose message */ 50 | #define IMQUIC_LOG_VERB (5) 51 | /*! \brief Overly verbose message */ 52 | #define IMQUIC_LOG_HUGE (6) 53 | /*! \brief Debug message (includes .c filename, function and line number) */ 54 | #define IMQUIC_LOG_DBG (7) 55 | /*! \brief Maximum level of debugging */ 56 | #define IMQUIC_LOG_MAX IMQUIC_LOG_DBG 57 | 58 | #pragma GCC diagnostic push 59 | #pragma GCC diagnostic ignored "-Wunused-variable" 60 | /*! \brief Coloured prefixes for errors and warnings logging. */ 61 | static const char *imquic_log_prefix[] = { 62 | /* no colors */ 63 | "", 64 | "[FATAL] ", 65 | "[ERR] ", 66 | "[WARN] ", 67 | "", 68 | "", 69 | "", 70 | "", 71 | /* with colors */ 72 | "", 73 | IMQUIC_ANSI_COLOR_MAGENTA"[FATAL]"IMQUIC_ANSI_COLOR_RESET" ", 74 | IMQUIC_ANSI_COLOR_RED"[ERR]"IMQUIC_ANSI_COLOR_RESET" ", 75 | IMQUIC_ANSI_COLOR_YELLOW"[WARN]"IMQUIC_ANSI_COLOR_RESET" ", 76 | "", 77 | "", 78 | "", 79 | "" 80 | }; 81 | ///@} 82 | #pragma GCC diagnostic pop 83 | 84 | /** @name imquic log wrappers 85 | */ 86 | ///@{ 87 | /*! \brief Simple wrapper to g_print/printf */ 88 | #define IMQUIC_PRINT g_print 89 | /*! \brief Logger based on different levels, which can either be displayed 90 | * or not according to the configuration of the server. 91 | * The format must be a string literal. */ 92 | #define IMQUIC_LOG(level, format, ...) \ 93 | do { \ 94 | if (level > IMQUIC_LOG_NONE && level <= IMQUIC_LOG_MAX && level <= imquic_log_level) { \ 95 | char imquic_log_ts[64] = ""; \ 96 | char imquic_log_src[128] = ""; \ 97 | if (imquic_log_timestamps) { \ 98 | struct tm imquictmresult; \ 99 | time_t imquicltime = time(NULL); \ 100 | localtime_r(&imquicltime, &imquictmresult); \ 101 | strftime(imquic_log_ts, sizeof(imquic_log_ts), \ 102 | "[%a %b %e %T %Y] ", &imquictmresult); \ 103 | } \ 104 | if (level == IMQUIC_LOG_FATAL || level == IMQUIC_LOG_ERR || level == IMQUIC_LOG_DBG) { \ 105 | snprintf(imquic_log_src, sizeof(imquic_log_src), \ 106 | "[%s:%s:%d] ", __FILE__, __FUNCTION__, __LINE__); \ 107 | } \ 108 | g_print("%s%s%s" format, \ 109 | imquic_log_ts, \ 110 | imquic_log_prefix[level | ((int)imquic_log_colors << 3)], \ 111 | imquic_log_src, \ 112 | ##__VA_ARGS__); \ 113 | } \ 114 | } while (0) 115 | ///@} 116 | 117 | #endif 118 | -------------------------------------------------------------------------------- /src/internal/buffer.h: -------------------------------------------------------------------------------- 1 | /*! \file buffer.h 2 | * \author Lorenzo Miniero 3 | * \copyright MIT License 4 | * \brief Buffer abstraction (headers) 5 | * \details Abstraction of a chunked buffer, to be used either during 6 | * CRYPTO exchanges in the Initial/Handshake phase, or with STREAM after 7 | * a connection has been established. It provides a high level interface 8 | * to adding chunks to the queue (in a gap-aware way), and to retrieving 9 | * data in an ordered way (waiting in case gaps are encountered). 10 | * 11 | * \ingroup Core 12 | */ 13 | 14 | #ifndef IMQUIC_BUFFER_H 15 | #define IMQUIC_BUFFER_H 16 | 17 | #include 18 | 19 | #include 20 | 21 | /*! \brief Buffer chunk */ 22 | typedef struct imquic_buffer_chunk { 23 | /*! \brief Data in this buffer chunk */ 24 | uint8_t *data; 25 | /*! \brief Offset this data is in, in the overall overall buffer */ 26 | uint64_t offset; 27 | /*! \brief Size of this this buffer chunk */ 28 | uint64_t length; 29 | } imquic_buffer_chunk; 30 | /*! \brief Helper to create a chunk out of existing data 31 | * @param data The data to put in the chunk 32 | * @param offset The offset of the data in the parent buffer 33 | * @param length The size of the data 34 | * @returns A pointer to a new imquic_buffer_chunk instance, if successful, or NULL otherwise */ 35 | imquic_buffer_chunk *imquic_buffer_chunk_create(uint8_t *data, uint64_t offset, uint64_t length); 36 | /*! \brief Helper to quickly free a buffer chunk 37 | * @param chunk The buffer chunk to free */ 38 | void imquic_buffer_chunk_free(imquic_buffer_chunk *chunk); 39 | 40 | /*! \brief Buffer made of multiple chunks (possibly with gaps) */ 41 | typedef struct imquic_buffer { 42 | /*! \brief Stream ID this buffer is associated with 43 | * @note Ignored when used for \c CRYPTO frames */ 44 | uint64_t stream_id; 45 | /*! \brief Ordered list of chunks in this buffer */ 46 | GList *chunks; 47 | /*! \brief Offset in the buffer to start from, when reading chunks from the buffer */ 48 | uint64_t base_offset; 49 | } imquic_buffer; 50 | /*! \brief Helper method to create a new buffer 51 | * @param stream_id Stream ID this buffer belongs to (ignored when used with \c CRYPTO frames) 52 | * @returns A pointer to a new imquic_buffer instance, if successful, or NULL otherwise */ 53 | imquic_buffer *imquic_buffer_create(uint64_t stream_id); 54 | /*! \brief Helper method to destroy an existing buffer 55 | * @param buf The imquic_buffer instance to destroy */ 56 | void imquic_buffer_destroy(imquic_buffer *buf); 57 | 58 | /*! \brief Helper method to add new data to the buffer at a specific offset, as a new chunk 59 | * @note In case the new data overlaps data already in the buffer, the new data is truncated accordingly to fit 60 | * @param buf The imquic_buffer instance to add data to 61 | * @param data The data to add to the buffer 62 | * @param offset Offset in the overall buffer where this new data should be placed 63 | * @param length Length of this new data 64 | * @returns The number of bytes actually added to the buffer in case of success, or 0 otherwise */ 65 | uint64_t imquic_buffer_put(imquic_buffer *buf, uint8_t *data, uint64_t offset, uint64_t length); 66 | /*! \brief Helper method to add new data at the end of the buffer, as a new chunk 67 | * @param buf The imquic_buffer instance to add data to 68 | * @param data The data to add to the buffer 69 | * @param length Length of this new data 70 | * @returns The number of bytes actually added to the buffer in case of success, or 0 otherwise */ 71 | uint64_t imquic_buffer_append(imquic_buffer *buf, uint8_t *data, uint64_t length); 72 | /*! \brief Helper method to peek at a buffer and check if there's data to read 73 | * @note If there's data in the buffer, but the current offset points at a gap 74 | * of data that hasn't been added yet, this function will not return anything, 75 | * since this buffer API is conceived to read data in an ordered way. If 76 | * data is found, the base offset is not modified, which means a new 77 | * imquic_buffer_peek call will return the same chunk until imquic_buffer_get 78 | * is called. 79 | * @param buf The imquic_buffer instance to peek 80 | * @returns A buffer chunk, if successful, or NULL otherwise */ 81 | imquic_buffer_chunk *imquic_buffer_peek(imquic_buffer *buf); 82 | /*! \brief Helper method to get a chunk of data from the buffer 83 | * @note If there's data in the buffer, but the current offset points at a gap 84 | * of data that hasn't been added yet, this function will not return anything, 85 | * since this buffer API is conceived to read data in an ordered way. If 86 | * data is found, the base offset is modified to point at the end of the 87 | * returned chunk, which means a new imquic_buffer_peek or imquic_buffer_get 88 | * call will point at the next chunk in the buffer. 89 | * @param buf The imquic_buffer instance to get the data from 90 | * @returns A buffer chunk, if successful, or NULL otherwise */ 91 | imquic_buffer_chunk *imquic_buffer_get(imquic_buffer *buf); 92 | 93 | /*! \brief Helper method to print the contents of a buffer 94 | * @param level Log level at which this should be printed 95 | * @param buf The imquic_buffer instance whose content should be printed */ 96 | void imquic_buffer_print(int level, imquic_buffer *buf); 97 | 98 | #endif 99 | -------------------------------------------------------------------------------- /src/internal/configuration.h: -------------------------------------------------------------------------------- 1 | /*! \file configuration.h 2 | * \author Lorenzo Miniero 3 | * \copyright MIT License 4 | * \brief imquic public interface 5 | * \details imquic configuration, to creare clients and servers. 6 | * 7 | * \ingroup Core 8 | */ 9 | 10 | #ifndef IMQUIC_CONFIGURATION_H 11 | #define IMQUIC_CONFIGURATION_H 12 | 13 | #include 14 | 15 | #include 16 | 17 | /*! \brief imquic initialization state */ 18 | typedef enum imquic_init_state { 19 | IMQUIC_UNINITIALIZED = -1, 20 | IMQUIC_NOT_INITIALIZED = 0, 21 | IMQUIC_INITIALIZING, 22 | IMQUIC_INITIALIZED 23 | } imquic_init_state; 24 | extern volatile int initialized; 25 | 26 | /*! \brief A client/server configuration */ 27 | typedef struct imquic_configuration { 28 | /*! \brief Name of the endpoint */ 29 | const char *name; 30 | /*! \brief Whether this is a server or a client */ 31 | gboolean is_server; 32 | /*! \brief Interface or IP address to bind to */ 33 | const char *ip; 34 | /*! \brief Local port of the endpoint */ 35 | uint16_t local_port; 36 | /*! \brief Remote address to connect to (client-only) */ 37 | const char *remote_host; 38 | /*! \brief Remote port to connect to (client-only) */ 39 | uint16_t remote_port; 40 | /*! \brief SNI to force, if any (will use localhost otherwise) */ 41 | const char *sni; 42 | /*! \brief ALPN to negotiate for raw QUIC */ 43 | const char *alpn; 44 | /*! \brief Whether raw QUIC should be offered 45 | * \note In case both \c raw_quic and \c webtranport are set to \c FALSE 46 | * the configuration will automatically default to raw QUIC only */ 47 | gboolean raw_quic; 48 | /*! \brief Whether WebTransport should be offered */ 49 | gboolean webtransport; 50 | /*! \brief In case WebTransport is used, the HTTP/3 path to connect to (client-only) */ 51 | const char *h3_path; 52 | /*! \brief In case WebTransport is used, the subprotocol to negotiate (currently unused) */ 53 | const char *subprotocol; 54 | /*! \brief Path to save QLOG files to, if needed/supported: a filename for clients, a folder for servers */ 55 | const char *qlog_path; 56 | /*! \brief Whether sequential JSON should be used for the QLOG file, instead of regular JSON */ 57 | gboolean qlog_sequential; 58 | /*! \brief Whether QUIC and/or HTTP/3 and/or RoQ and/or MoQT events should be saved to QLOG, if supported */ 59 | gboolean qlog_quic, qlog_http3, qlog_roq, qlog_moq; 60 | /*! \brief Path to the certificate file to use for TLS */ 61 | const char *cert_pem; 62 | /*! \brief Path to the key file to use for TLS */ 63 | const char *cert_key; 64 | /*! \brief Password needed to access the certificate for TLS, if any */ 65 | const char *cert_pwd; 66 | /*! \brief Whether early data should be supported */ 67 | gboolean early_data; 68 | /*! \brief File to use for session tickets, when doing early data */ 69 | const char *ticket_file; 70 | /*! \brief Optional user data, to pass back when notifying new connections associated to this endpoint */ 71 | void *user_data; 72 | } imquic_configuration; 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /src/internal/error.h: -------------------------------------------------------------------------------- 1 | /*! \file error.h 2 | * \author Lorenzo Miniero 3 | * \copyright MIT License 4 | * \brief QUIC errors definitions (headers) 5 | * \details Definitions for QUIC errors, and helper functions to use them. 6 | * 7 | * \ingroup Core 8 | */ 9 | 10 | #ifndef IMQUIC_ERROR_H 11 | #define IMQUIC_ERROR_H 12 | 13 | /*! \brief QUIC error codes */ 14 | typedef enum imquic_error_code { 15 | IMQUIC_NO_ERROR = 0x00, 16 | IMQUIC_INTERNAL_ERROR = 0x01, 17 | IMQUIC_CONNECTION_REFUSED = 0x02, 18 | IMQUIC_FLOW_CONTROL_ERROR = 0x03, 19 | IMQUIC_STREAM_LIMIT_ERROR = 0x04, 20 | IMQUIC_STREAM_STATE_ERROR = 0x05, 21 | IMQUIC_FINAL_SIZE_ERROR = 0x06, 22 | IMQUIC_FRAME_ENCODING_ERROR = 0x07, 23 | IMQUIC_TRANSPORT_PARAMETER_ERROR = 0x08, 24 | IMQUIC_CONNECTION_ID_LIMIT_ERROR = 0x09, 25 | IMQUIC_PROTOCOL_VIOLATION = 0x0A, 26 | IMQUIC_INVALID_TOKEN = 0x0B, 27 | IMQUIC_APPLICATION_ERROR = 0x0C, 28 | IMQUIC_CRYPTO_BUFFER_EXCEEDED = 0x0D, 29 | IMQUIC_KEY_UPDATE_ERROR = 0x0E, 30 | IMQUIC_AEAD_LIMIT_REACHED = 0x0F, 31 | IMQUIC_NO_VIABLE_PATH = 0x10, 32 | IMQUIC_CRYPTO_ERROR = 0x0100 33 | } imquic_error_code; 34 | /*! \brief Helper function to serialize to string the name of a imquic_error_code value. 35 | * @param type The imquic_error_code value 36 | * @returns The code name as a string, if valid, or NULL otherwise */ 37 | const char *imquic_error_code_str(imquic_error_code type); 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /src/internal/listmap.h: -------------------------------------------------------------------------------- 1 | /*! \file listmap.h 2 | * \author Lorenzo Miniero 3 | * \copyright MIT License 4 | * \brief Combined list and map utility (headers) 5 | * \details Implementation of a generic structure that contains properties 6 | * both of a map and a linked list (with quick pointers to the head to 7 | * work a bit like a queue, when appending). Mostly to be used for tracking 8 | * in an efficient way resources that must be ordered, but also quickly 9 | * accessed via a key (e.g., sent packets by packet number). 10 | * 11 | * \ingroup Core 12 | */ 13 | 14 | #ifndef IMQUIC_LISTMAP_H 15 | #define IMQUIC_LISTMAP_H 16 | 17 | #include 18 | 19 | /*! \brief Type of keys for the map part, to figure out the hashing algorithm */ 20 | typedef enum imquic_listmap_key { 21 | /*! \brief Generic number (up to 32-bits) */ 22 | IMQUIC_LISTMAP_NUMBER = 0, 23 | /*! \brief String */ 24 | IMQUIC_LISTMAP_STRING, 25 | /*! \brief 64-bit number */ 26 | IMQUIC_LISTMAP_NUMBER64, 27 | } imquic_listmap_key; 28 | 29 | /*! \brief Utility that implements a list and a map at the same time */ 30 | typedef struct imquic_listmap { 31 | /*! \brief Key type */ 32 | imquic_listmap_key type; 33 | /*! \brief Linked list */ 34 | GList *list; 35 | /*! \brief Current index in the list, when traversing */ 36 | GList *index; 37 | /*! \brief Last item in the linked list (for appending without traversing the whole list) */ 38 | GList *last; 39 | /*! \brief Hashtable to implement the map part */ 40 | GHashTable *table; 41 | /*! \brief Number of items in the listmap */ 42 | int length; 43 | /*! \brief Function to invoke when a stored item is removed */ 44 | GDestroyNotify destroy; 45 | } imquic_listmap; 46 | /*! \brief Create a new imquic_listmap instance 47 | * @param type The imquic_listmap_key key type 48 | * @param destroy The function to invoke when a stored item is removed 49 | * @returns A pointer to a new imquic_listmap instance, or NULL otherwise */ 50 | imquic_listmap *imquic_listmap_create(imquic_listmap_key type, GDestroyNotify destroy); 51 | /*! \brief Destroy an existing imquic_listmap instance 52 | * @param lm The imquic_listmap instance to destroy */ 53 | void imquic_listmap_destroy(imquic_listmap *lm); 54 | 55 | /*! \brief Add a new item at the beginning of a imquic_listmap instance 56 | * @note Adding items to a listmap resets the traversing index 57 | * @param lm The imquic_listmap instance to prepend the item to 58 | * @param key The item key, for the map 59 | * @param item The item to add to the listmap 60 | * @returns 0 if successful, or a negative integer otherwise */ 61 | int imquic_listmap_prepend(imquic_listmap *lm, void *key, void *item); 62 | /*! \brief Add a new item at the end of a imquic_listmap instance 63 | * @note Adding items to a listmap resets the traversing index 64 | * @param lm The imquic_listmap instance to append the item to 65 | * @param key The item key, for the map 66 | * @param item The item to add to the listmap 67 | * @returns 0 if successful, or a negative integer otherwise */ 68 | int imquic_listmap_append(imquic_listmap *lm, void *key, void *item); 69 | /*! \brief Remove an item from a imquic_listmap instance 70 | * @note Removing items from a listmap resets the traversing index 71 | * @param lm The imquic_listmap instance to remove the item from 72 | * @param key The key of the item to remove 73 | * @returns 0 if successful, or a negative integer otherwise */ 74 | int imquic_listmap_remove(imquic_listmap *lm, void *key); 75 | /*! \brief Remove all items from a imquic_listmap instance 76 | * @param lm The imquic_listmap instance to clear 77 | * @returns 0 if successful, or a negative integer otherwise */ 78 | int imquic_listmap_clear(imquic_listmap *lm); 79 | 80 | /*! \brief Look for an item via its key 81 | * @param lm The imquic_listmap instance to lookup 82 | * @param key The item key to lookup 83 | * @returns A pointer to the item, if found, or NULL otherwise */ 84 | void *imquic_listmap_find(imquic_listmap *lm, void *key); 85 | /*! \brief Check if a listmap contains a certain item 86 | * @param lm The imquic_listmap instance to search 87 | * @param item The item to find 88 | * @returns TRUE if found, or FALSE otherwise */ 89 | gboolean imquic_listmap_contains(imquic_listmap *lm, void *item); 90 | 91 | /*! \brief Start traversing a listmap in an ordered way from the start 92 | * @note This only resets the list: use imquic_listmap_traverse_next 93 | * and imquic_listmap_traverse_prev to actually access the items 94 | * @param lm The imquic_listmap to start traversing */ 95 | void imquic_listmap_traverse(imquic_listmap *lm); 96 | /*! \brief Get the next item from the listmap, assuming we're traversing 97 | * @param[in] lm The imquic_listmap to get the next item from 98 | * @param[out] found Set to TRUE if the item was found (to avoid ambiguities 99 | * if the item itself is 0 or NULL) 100 | * @returns A pointer to the item */ 101 | void *imquic_listmap_next(imquic_listmap *lm, gboolean *found); 102 | /*! \brief Get the previous item from the listmap, assuming we're traversing 103 | * @param[in] lm The imquic_listmap to get the previous item from 104 | * @param[out] found Set to TRUE if the item was found (to avoid ambiguities 105 | * if the item itself is 0 or NULL) 106 | * @returns A pointer to the item */ 107 | void *imquic_listmap_prev(imquic_listmap *lm, gboolean *found); 108 | 109 | 110 | #endif 111 | -------------------------------------------------------------------------------- /src/internal/loop.h: -------------------------------------------------------------------------------- 1 | /*! \file loop.h 2 | * \author Lorenzo Miniero 3 | * \copyright MIT License 4 | * \brief Event loop (headers) 5 | * \details Implementation of an event loop to be used in the imquic 6 | * library internals, mostly for networking and the dispatching of some 7 | * events. 8 | * 9 | * \todo At the moment, a single event loop is created a startup that 10 | * all connections share. Besides, the events that can be dispatched 11 | * are defined in a suboptimal way that will need to be fixed. 12 | * 13 | * \ingroup Core 14 | */ 15 | 16 | #ifndef IMQUIC_LOOP_H 17 | #define IMQUIC_LOOP_H 18 | 19 | #include 20 | 21 | #include 22 | 23 | #include "network.h" 24 | 25 | /*! \brief Initialize the event loop */ 26 | int imquic_loop_init(void); 27 | /*! \brief Helper method to wake the event loop, in case it's waiting 28 | * for something and we need to refresh the list of events to monitor */ 29 | void imquic_loop_wakeup(void); 30 | /*! \brief Uninitialize the event loop */ 31 | void imquic_loop_deinit(void); 32 | 33 | /*! \brief Event source base */ 34 | typedef struct imquic_source { 35 | /*! \brief All event sources are actually extensions of the GLib \c GSource */ 36 | GSource parent; 37 | } imquic_source; 38 | 39 | /*! \brief Events meant for connections */ 40 | typedef struct imquic_connection_event { 41 | /* \todo Refactor the event loop */ 42 | } imquic_connection_event; 43 | 44 | /** @name Adding sources to the loop 45 | */ 46 | ///@{ 47 | /*! \brief Monitor an endpoint socket as part of the loop 48 | * @param e Opaque pointer to a imquic_network_endpoint instance 49 | * @returns A pointer to the imquic_source, if successful, or NULL otherwise */ 50 | imquic_source *imquic_loop_poll_endpoint(void *e); 51 | /*! \brief Monitor events associated to a connection in the core as part of the loop 52 | * @param c Opaque pointer to a imquic_connection instance 53 | * @returns A pointer to the imquic_source, if successful, or NULL otherwise */ 54 | imquic_source *imquic_loop_poll_connection(void *c); 55 | /*! \brief Helper method to add a timed source to the loop, to fire 56 | * the provided callback every tot milliseconds and passing the provided data. 57 | * @param ms Call the callback every tot milliseconds 58 | * @param func Callback to invoke when the regular timer fires 59 | * @param data Optional user data to pass to the callback function, for correlation purposes 60 | * @returns A pointer to the imquic_source, if successful, or NULL otherwise */ 61 | imquic_source *imquic_loop_add_timer(guint ms, GSourceFunc func, gpointer data); 62 | ///@} 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /src/internal/mutex.h: -------------------------------------------------------------------------------- 1 | /*! \file mutex.h 2 | * \author Lorenzo Miniero 3 | * \copyright MIT License 4 | * \brief Semaphores, Mutexes and Conditions 5 | * \details Implementation (based on GMutex) of a locking mechanism based on mutexes and conditions. 6 | * 7 | * \ingroup Core 8 | */ 9 | 10 | #ifndef IMQUIC_MUTEX_H 11 | #define IMQUIC_MUTEX_H 12 | 13 | #include "../imquic/debug.h" 14 | 15 | extern gboolean imquic_lock_debug; 16 | 17 | /*! \brief imquic mutex implementation */ 18 | typedef GMutex imquic_mutex; 19 | /*! \brief imquic mutex initialization */ 20 | #define imquic_mutex_init(a) g_mutex_init(a) 21 | /*! \brief imquic static mutex initializer */ 22 | #define IMQUIC_MUTEX_INITIALIZER {0} 23 | /*! \brief imquic mutex destruction */ 24 | #define imquic_mutex_destroy(a) g_mutex_clear(a) 25 | /*! \brief imquic mutex lock without debug */ 26 | #define imquic_mutex_lock_nodebug(a) g_mutex_lock(a) 27 | /*! \brief imquic mutex lock with debug (prints the line that locked a mutex) */ 28 | #define imquic_mutex_lock_debug(a) { IMQUIC_PRINT("[%s:%s:%d:lock] %p\n", __FILE__, __FUNCTION__, __LINE__, a); g_mutex_lock(a); } 29 | /*! \brief imquic mutex lock wrapper (selective locking debug) */ 30 | #define imquic_mutex_lock(a) { if(!imquic_lock_debug) { imquic_mutex_lock_nodebug(a); } else { imquic_mutex_lock_debug(a); } } 31 | /*! \brief imquic mutex try lock without debug */ 32 | #define imquic_mutex_trylock_nodebug(a) { ret = g_mutex_trylock(a); } 33 | /*! \brief imquic mutex try lock with debug (prints the line that tried to lock a mutex) */ 34 | #define imquic_mutex_trylock_debug(a) { IMQUIC_PRINT("[%s:%s:%d:trylock] %p\n", __FILE__, __FUNCTION__, __LINE__, a); ret = g_mutex_trylock(a); } 35 | /*! \brief imquic mutex try lock wrapper (selective locking debug) */ 36 | #define imquic_mutex_trylock(a) ({ gboolean ret; if(!imquic_lock_debug) { imquic_mutex_trylock_nodebug(a); } else { imquic_mutex_trylock_debug(a); } ret; }) 37 | /*! \brief imquic mutex unlock without debug */ 38 | #define imquic_mutex_unlock_nodebug(a) g_mutex_unlock(a) 39 | /*! \brief imquic mutex unlock with debug (prints the line that unlocked a mutex) */ 40 | #define imquic_mutex_unlock_debug(a) { IMQUIC_PRINT("[%s:%s:%d:unlock] %p\n", __FILE__, __FUNCTION__, __LINE__, a); g_mutex_unlock(a); } 41 | /*! \brief imquic mutex unlock wrapper (selective locking debug) */ 42 | #define imquic_mutex_unlock(a) { if(!imquic_lock_debug) { imquic_mutex_unlock_nodebug(a); } else { imquic_mutex_unlock_debug(a); } } 43 | 44 | /*! \brief imquic condition implementation */ 45 | typedef GCond imquic_condition; 46 | /*! \brief imquic condition initialization */ 47 | #define imquic_condition_init(a) g_cond_init(a) 48 | /*! \brief imquic condition destruction */ 49 | #define imquic_condition_destroy(a) g_cond_clear(a) 50 | /*! \brief imquic condition wait */ 51 | #define imquic_condition_wait(a, b) g_cond_wait(a, b); 52 | /*! \brief imquic condition wait until */ 53 | #define imquic_condition_wait_until(a, b, c) g_cond_wait_until(a, b, c); 54 | /*! \brief imquic condition signal */ 55 | #define imquic_condition_signal(a) g_cond_signal(a); 56 | /*! \brief imquic condition broadcast */ 57 | #define imquic_condition_broadcast(a) g_cond_broadcast(a); 58 | 59 | #endif 60 | -------------------------------------------------------------------------------- /src/internal/network.h: -------------------------------------------------------------------------------- 1 | /*! \file network.h 2 | * \author Lorenzo Miniero 3 | * \copyright MIT License 4 | * \brief Networking utilities (headers) 5 | * \details Implementation of the networking functionality of the QUIC 6 | * stack. This is where client and server instances are allocated and 7 | * managed, taking care of actually sending data out, and to notify upper 8 | * layers about new connections or data coming in. The networking stack 9 | * relies on a separate event loop for polling the sockets. 10 | * 11 | * \ingroup Core 12 | */ 13 | 14 | #ifndef IMQUIC_NETWORK_H 15 | #define IMQUIC_NETWORK_H 16 | 17 | #include 18 | #include 19 | 20 | #include "../imquic/imquic.h" 21 | #include "configuration.h" 22 | #include "loop.h" 23 | #include "crypto.h" 24 | #include "moq.h" 25 | #include "roq.h" 26 | #include "refcount.h" 27 | 28 | /*! \brief Initialize the network stack at startup */ 29 | void imquic_network_init(void); 30 | /*! \brief Uninitialize the network stack */ 31 | void imquic_network_deinit(void); 32 | 33 | /*! \brief Abstraction of a network address */ 34 | typedef struct imquic_network_address { 35 | /*! \brief Network address */ 36 | struct sockaddr_storage addr; 37 | /*! \brief Size of the network address */ 38 | socklen_t addrlen; 39 | } imquic_network_address; 40 | /*! \brief Helper to serialize a network address to a string 41 | * @param[in] address The imquic_network_address instance to serialize 42 | * @param[out] output The buffer to put the serialized string into 43 | * @param[in] outlen The size of the output buffer 44 | * @param[in] add_port Whether the port should be added to the string 45 | * @returns A pointer to output, if successful, or NULL otherwise */ 46 | char *imquic_network_address_str(imquic_network_address *address, char *output, size_t outlen, gboolean add_port); 47 | /*! \brief Helper to return the port used by a network address 48 | * @param[in] address The imquic_network_address instance to query 49 | * @returns A port number, if successful, or -1 otherwise */ 50 | uint16_t imquic_network_address_port(imquic_network_address *address); 51 | 52 | /*! \brief Abstraction of a network endpoint (client or server) */ 53 | typedef struct imquic_network_endpoint { 54 | /*! brief Opaque pointer to the source owning this socket (to handle it in the loop) */ 55 | void *source; 56 | /*! \brief Name of this endpoint */ 57 | char *name; 58 | /*! \brief Whether this is a client or a server */ 59 | gboolean is_server; 60 | /*! \brief Socket */ 61 | int fd; 62 | /*! \brief Local and remote ports */ 63 | uint16_t port, remote_port; 64 | /*! \brief Local address */ 65 | imquic_network_address local_address; 66 | /*! \brief Remote address of the peer (clients only) */ 67 | imquic_network_address remote_address; 68 | /*! \brief TLS stack */ 69 | imquic_tls *tls; 70 | /*! \brief SNI the client will use */ 71 | char *sni; 72 | /*! \brief Whether raw QUIC should be supported */ 73 | gboolean raw_quic; 74 | /*! \brief ALPN this endpoint will negotiate, when using raw QUIC */ 75 | char *alpn; 76 | /*! \brief Whether WebTransport should be supported */ 77 | gboolean webtransport; 78 | /*! \brief For WebTransport clients, the path to \c CONNECT to (\c / by default) */ 79 | char *h3_path; 80 | /*! \brief In case WebTransport is used, subprotocol to negotiate (currently unused) */ 81 | char *subprotocol; 82 | /*! \brief List of connections handled by this socket (may be more than one for servers) */ 83 | GHashTable *connections; 84 | /*! \brief Number of connections handled by this socket (may be more than one for servers) */ 85 | uint64_t conns_num; 86 | /*! \brief Whether this endpoint has internal generic callbacks (true for the native RoQ and MoQ stacks) */ 87 | gboolean internal_callbacks; 88 | /*! \brief Callback to invoke when a new connection is available on this endpoint */ 89 | void (* new_connection)(imquic_connection *conn, void *user_data); 90 | /*! \brief Callback to invoke when new \c STREAM data is available on one of the connections handled by this endpoint */ 91 | void (* stream_incoming)(imquic_connection *conn, uint64_t stream_id, 92 | uint8_t *bytes, uint64_t offset, uint64_t length, gboolean complete); 93 | /*! \brief Callback to invoke when new \c DATAGRAM data is available on one of the connections handled by this endpoint */ 94 | void (* datagram_incoming)(imquic_connection *conn, uint8_t *bytes, uint64_t length); 95 | /*! \brief Callback to invoke when new one of the connections handled by this endpoint is closed */ 96 | void (* connection_gone)(imquic_connection *conn); 97 | /*! \brief Callback to invoke when a client connection attempt fails */ 98 | void (* connection_failed)(void *user_data); 99 | /*! \brief User data to pass in the \c new_connection callback, to correlate a connection to the endpoint it's coming from */ 100 | void *user_data; 101 | /*! \brief (Sub-)Protocol this endpoint uses, in case imquic is handling a protocol natively */ 102 | uint64_t protocol; 103 | /*! \brief (Sub-)Protocol specific callbacks (at the time of writing, RoQ and MoQ only) */ 104 | union { 105 | imquic_moq_callbacks moq; 106 | imquic_roq_callbacks roq; 107 | } callbacks; 108 | /*! \brief Path to save QLOG files to, if needed/supported: a filename for clients, a folder for servers */ 109 | char *qlog_path; 110 | /*! \brief Whether sequential JSON should be used for the QLOG file, instead of regular JSON */ 111 | gboolean qlog_sequential; 112 | /*! \brief Whether QUIC and/or HTTP/3 and/or RoQ and/or MoQT events should be saved to QLOG, if supported */ 113 | gboolean qlog_quic, qlog_http3, qlog_roq, qlog_moq; 114 | /*! \brief Mutex */ 115 | imquic_mutex mutex; 116 | /*! \brief Whether this connection has been started */ 117 | volatile gint started; 118 | /*! \brief Whether this connection is being shut down */ 119 | volatile gint shutting; 120 | /*! \brief Whether this instance has been destroyed (reference counting) */ 121 | volatile gint destroyed; 122 | /*! \brief Reference counter */ 123 | imquic_refcount ref; 124 | } imquic_network_endpoint; 125 | /*! \brief Helper to create a new imquic_network_endpoint instance from a imquic_configuration object 126 | * @param config The imquic_configuration object to use to configure and create the new endpoint 127 | * @returns A pointer to a new imquic_network_endpoint instance, if successful, or NULL otherwise */ 128 | imquic_network_endpoint *imquic_network_endpoint_create(imquic_configuration *config); 129 | /*! \brief Helper to add a new connection to the list of connections originated by this endpoint 130 | * @param ne The imquic_network_endpoint instance to add the connection to 131 | * @param conn The imquic_connection instance to add to the endpoint 132 | * @param lock_mutex Whether the endpoint mutex should be used to protect the action (to avoid double locks) */ 133 | void imquic_network_endpoint_add_connection(imquic_network_endpoint *ne, imquic_connection *conn, gboolean lock_mutex); 134 | /*! \brief Helper to remove an existing connection from the list of connections originated by this endpoint 135 | * @param ne The imquic_network_endpoint instance to remove the connection from 136 | * @param conn The imquic_connection instance to remove from the endpoint 137 | * @param lock_mutex Whether the endpoint mutex should be used to protect the action (to avoid double locks) */ 138 | void imquic_network_endpoint_remove_connection(imquic_network_endpoint *ne, imquic_connection *conn, gboolean lock_mutex); 139 | /*! \brief Helper to shutdown an existing endpoint 140 | * @param ne The imquic_network_endpoint instance to shut down */ 141 | void imquic_network_endpoint_shutdown(imquic_network_endpoint *ne); 142 | /*! \brief Helper to destroy an existing endpoint instance 143 | * @param ne The imquic_network_endpoint instance to destroy */ 144 | void imquic_network_endpoint_destroy(imquic_network_endpoint *ne); 145 | 146 | /*! \brief Helper to send data on a connection 147 | * @param conn The imquic_connection instance to send the data on 148 | * @param bytes The data to send 149 | * @param blen the size of the data to send 150 | * @returns 0 in case of success, a negative integer otherwise */ 151 | int imquic_network_send(imquic_connection *conn, uint8_t *bytes, size_t blen); 152 | 153 | #endif 154 | -------------------------------------------------------------------------------- /src/internal/qpack.h: -------------------------------------------------------------------------------- 1 | /*! \file qpack.h 2 | * \author Lorenzo Miniero 3 | * \copyright MIT License 4 | * \brief QPACK stack (WebTransport only) (headers) 5 | * \details Naive implementation of QPACK, which implements static and 6 | * dynamic tables, and Huffman encoding/decoding via static tables. This 7 | * code is only used for the WebTransport establishment via HTTP/3. 8 | * 9 | * \ingroup Core 10 | */ 11 | 12 | #ifndef IMQUIC_QPACK_H 13 | #define IMQUIC_QPACK_H 14 | 15 | #include 16 | 17 | #include 18 | 19 | /*! \brief Name-value entry we can have in the static or dynamic tables */ 20 | typedef struct imquic_qpack_entry { 21 | /*! \brief ID (for static or dynamic table) */ 22 | uint16_t id; 23 | /*! \brief Name */ 24 | const char *name; 25 | /*! \brief Value */ 26 | const char *value; 27 | } imquic_qpack_entry; 28 | /*! \brief Create a new entry out of provided name and value 29 | * @param name Name of the new entry 30 | * @param value Value of the new entry 31 | * @returns A pointer to a new imquic_qpack_entry instance, if successful, or NULL otherwise */ 32 | imquic_qpack_entry *imquic_qpack_entry_create(const char *name, const char *value); 33 | /*! \brief Helper to calculate the size of this entry 34 | * @param entry Entry instance to calculate the size for 35 | * @returns The size of the entry, as needed for the dynamic table size */ 36 | size_t imquic_qpack_entry_size(imquic_qpack_entry *entry); 37 | /*! \brief Destroy an existing entry 38 | * @param entry Entry instance to destroy */ 39 | void imquic_qpack_entry_destroy(imquic_qpack_entry *entry); 40 | 41 | /*! \brief Static table */ 42 | extern imquic_qpack_entry imquic_qpack_static_table[]; 43 | 44 | /*! \brief Dynamic table */ 45 | typedef struct imquic_qpack_dynamic_table { 46 | /*! \brief Size as advertized, and current size */ 47 | size_t capacity, size; 48 | /*! \brief Current index */ 49 | uint16_t index; 50 | /*! \brief Hashtable (indexed by ID) */ 51 | GHashTable *table_byid; 52 | /*! \brief List of entries, ordered by insertion */ 53 | GList *list; 54 | } imquic_qpack_dynamic_table; 55 | /*! \brief Create a new dynamic table 56 | * @param capacity The capacity of the dynamic table, as advertized 57 | * @returns A pointer to a new imquic_qpack_dynamic_table instance, if successful, or NULL otherwise */ 58 | imquic_qpack_dynamic_table *imquic_qpack_dynamic_table_create(size_t capacity); 59 | /*! \brief Destroy an existing dynamic table 60 | * @param table Dynamic table instance to destroy */ 61 | void imquic_qpack_dynamic_table_destroy(imquic_qpack_dynamic_table *table); 62 | 63 | /*! \brief QPACK context */ 64 | typedef struct imquic_qpack_context { 65 | /*! \brief Local dynamic table (updated by us via the local encoder stream) */ 66 | imquic_qpack_dynamic_table *ltable; 67 | /*! \brief Remote dynamic table (updated by the remote encoder stream) */ 68 | imquic_qpack_dynamic_table *rtable; 69 | } imquic_qpack_context; 70 | /*! \brief Create a new QPACK context 71 | * @param capacity The capacity of the dynamic table, as advertized 72 | * @returns A pointer to a new imquic_qpack_context instance, if successful, or NULL otherwise */ 73 | imquic_qpack_context *imquic_qpack_context_create(size_t capacity); 74 | /*! \brief Destroy an existing QPACK context 75 | * @param ctx Context instance to destroy */ 76 | void imquic_qpack_context_destroy(imquic_qpack_context *ctx); 77 | 78 | /** @name Interacting with QPACK 79 | */ 80 | ///@{ 81 | /*! \brief Decode incoming QPACK encoder data 82 | * @note This is data coming from the encoder stream of our peer 83 | * @param ctx The imquic_qpack_context to update with the new encoder data 84 | * @param bytes The buffer containing the encoder data 85 | * @param blen Size of the encoder data 86 | * @returns The amount of processed encoder data */ 87 | size_t imquic_qpack_decode(imquic_qpack_context *ctx, uint8_t *bytes, size_t blen); 88 | /*! \brief Decode an incoming QPACK request 89 | * @note This is data coming from a request. The GList and its contents 90 | * are owned by the caller, and so should be freed when not needed anymore. 91 | * @param[in] ctx The imquic_qpack_context to refer to 92 | * @param[in] bytes The buffer containing the request data 93 | * @param[in] blen Size of the request data 94 | * @param[out] bread The amount of processed request data 95 | * @returns A list of imquic_qpack_entry entries obtained from the request */ 96 | GList *imquic_qpack_process(imquic_qpack_context *ctx, uint8_t *bytes, size_t blen, size_t *bread); 97 | /*! \brief Encode outgoing QPACK encoder data 98 | * @note This is data we'll send on our encoder stream 99 | * @param[in] ctx The imquic_qpack_context to use to encode the data 100 | * @param[in] headers List of headers to encode 101 | * @param[in] bytes The buffer to put the encoded data in 102 | * @param[out] blen Size of the encoded data buffer 103 | * @param[in] qenc The buffer to put the QPACK encoder info in, if any 104 | * @param[out] qenclen Size of the QPACK encoder info buffer 105 | * @returns 0 in case of success, a negative integer otherwise */ 106 | int imquic_qpack_encode(imquic_qpack_context *ctx, GList *headers, uint8_t *bytes, size_t *blen, uint8_t *qenc, size_t *qenclen); 107 | ///@} 108 | 109 | #endif 110 | -------------------------------------------------------------------------------- /src/internal/refcount.h: -------------------------------------------------------------------------------- 1 | /*! \file refcount.h 2 | * \author Lorenzo Miniero 3 | * \copyright MIT License 4 | * \brief Reference counter mechanism 5 | * \details Implementation of a simple reference counter that can be 6 | * used to keep track of memory management in imquic, in order to avoid 7 | * the need for timed garbage collectord and the like which have proven 8 | * ineffective in the past (e.g., crashes whenever race conditions 9 | * occurred). This implementation is heavily based on an excellent 10 | * blog post 11 | * written by Chris Wellons. 12 | * 13 | * Objects interested in leveraging this reference counter mechanism 14 | * must add a imquic_refcount instance as one of the members of the object 15 | * itself, and then call imquic_refcall_init() to set it up. Initializing 16 | * the reference counter just needs a pointer to the function to invoke 17 | * when the object needs to be destroyed (counter reaches 0), while it 18 | * will automatically set the counter to 1. To increase and decrease the 19 | * counter just call imquic_refcount_increase() and imquic_refcount_decrease(). 20 | * When the counter reaches 0, the function passed when initializing it will 21 | * be invoked: this means it's up to you to then free all the resources 22 | * the object may have allocated. Notice that if this involves other 23 | * objects that are reference counted, freeing the resource will just 24 | * mean decreasing the related counter, and not destroying it right away. 25 | * 26 | * The free function must be defined like this: 27 | * 28 | \verbatim 29 | void my_free_function(imquic_refcount *counter); 30 | \endverbatim 31 | * 32 | * Since the reference counter cannot know the size of the object to be 33 | * freed, or where in the list of members the counter has been placed, 34 | * retrieving the pointer to the object to free is up to you, using the 35 | * imquic_refcount_containerof macro. This is an example of how the 36 | * free function we have defined above may be implemented: 37 | * 38 | \verbatim 39 | typedef my_struct { 40 | int number; 41 | char *string; 42 | imquic_refcount myref; 43 | } 44 | 45 | void my_free_function(imquic_refcount *counter) { 46 | struct my_struct *my_object = imquic_refcount_containerof(counter, struct my_struct, myref); 47 | if(my_object->string) 48 | free(my_object->string); 49 | free(my_object); 50 | } 51 | \endverbatim 52 | * 53 | * \ingroup Core 54 | */ 55 | 56 | #ifndef IMQUIC_REFCOUNT_H 57 | #define IMQUIC_REFCOUNT_H 58 | 59 | #include 60 | #include "mutex.h" 61 | 62 | //~ #define IMQUIC_REFCOUNT_DEBUG 63 | 64 | extern int imquic_refcount_debug; 65 | 66 | /*! \brief Macro to programmatically address the object itself from its counter 67 | * \details \c refptr is the pointer to the imquic_refcount instance, \c type 68 | * is the type of the object itself (e.g., struct mystruct), 69 | * while \c member is how the imquic_refcount instance is called in the 70 | * object that contains it. */ 71 | #define imquic_refcount_containerof(refptr, type, member) \ 72 | ((type *)((char *)(refptr) - offsetof(type, member))) 73 | 74 | 75 | /*! \brief imquic reference counter structure */ 76 | typedef struct imquic_refcount imquic_refcount; 77 | struct imquic_refcount { 78 | /*! \brief The reference counter itself */ 79 | gint count; 80 | /*! \brief Pointer to the function that will be used to free the object */ 81 | void (*free)(const imquic_refcount *); 82 | }; 83 | 84 | #ifdef IMQUIC_REFCOUNT_DEBUG 85 | /* Reference counters debugging */ 86 | extern GHashTable *imquic_counters; 87 | extern imquic_mutex imquic_counters_mutex; 88 | #define imquic_refcount_track(refp) { \ 89 | imquic_mutex_lock(&imquic_counters_mutex); \ 90 | if(imquic_counters == NULL) \ 91 | imquic_counters = g_hash_table_new(NULL, NULL); \ 92 | g_hash_table_insert(imquic_counters, refp, refp); \ 93 | imquic_mutex_unlock(&imquic_counters_mutex); \ 94 | } 95 | #define imquic_refcount_untrack(refp) { \ 96 | imquic_mutex_lock(&imquic_counters_mutex); \ 97 | g_hash_table_remove(imquic_counters, refp); \ 98 | imquic_mutex_unlock(&imquic_counters_mutex); \ 99 | } 100 | #endif 101 | 102 | 103 | /*! \brief imquic reference counter initialization (debug according to settings) 104 | * \note Also sets the counter to 1 automatically, so no need to increase 105 | * it again manually via imquic_refcount_increase() after the initialization 106 | * @param refp Pointer to the imquic reference counter instance 107 | * @param free_fn Pointer to the function to invoke when the object the counter 108 | * refers to needs to be destroyed */ 109 | #define imquic_refcount_init(refp, free_fn) { \ 110 | if(!imquic_refcount_debug) { \ 111 | imquic_refcount_init_nodebug(refp, free_fn); \ 112 | } else { \ 113 | imquic_refcount_init_debug(refp, free_fn); \ 114 | } \ 115 | } 116 | /*! \brief imquic reference counter initialization (no debug) 117 | * \note Also sets the counter to 1 automatically, so no need to increase 118 | * it again manually via imquic_refcount_increase() after the initialization 119 | * @param refp Pointer to the imquic reference counter instance 120 | * @param free_fn Pointer to the function to invoke when the object the counter 121 | * refers to needs to be destroyed */ 122 | #ifdef IMQUIC_REFCOUNT_DEBUG 123 | #define imquic_refcount_init_nodebug(refp, free_fn) { \ 124 | (refp)->count = 1; \ 125 | (refp)->free = free_fn; \ 126 | imquic_refcount_track((refp)); \ 127 | } 128 | #else 129 | #define imquic_refcount_init_nodebug(refp, free_fn) { \ 130 | (refp)->count = 1; \ 131 | (refp)->free = free_fn; \ 132 | } 133 | #endif 134 | /*! \brief imquic reference counter initialization (debug) 135 | * \note Also sets the counter to 1 automatically, so no need to increase 136 | * it again manually via imquic_refcount_increase() after the initialization 137 | * @param refp Pointer to the imquic reference counter instance 138 | * @param free_fn Pointer to the function to invoke when the object the counter 139 | * refers to needs to be destroyed */ 140 | #ifdef IMQUIC_REFCOUNT_DEBUG 141 | #define imquic_refcount_init_debug(refp, free_fn) { \ 142 | (refp)->count = 1; \ 143 | IMQUIC_PRINT("[%s:%s:%d:init] %p (%d)\n", __FILE__, __FUNCTION__, __LINE__, refp, (refp)->count); \ 144 | (refp)->free = free_fn; \ 145 | imquic_refcount_track((refp)); \ 146 | } 147 | #else 148 | #define imquic_refcount_init_debug(refp, free_fn) { \ 149 | (refp)->count = 1; \ 150 | IMQUIC_PRINT("[%s:%s:%d:init] %p (%d)\n", __FILE__, __FUNCTION__, __LINE__, refp, (refp)->count); \ 151 | (refp)->free = free_fn; \ 152 | } 153 | #endif 154 | 155 | /*! \brief Increase the imquic reference counter (debug according to settings) 156 | * @param refp Pointer to the imquic reference counter instance */ 157 | #define imquic_refcount_increase(refp) { \ 158 | if(!imquic_refcount_debug) { \ 159 | imquic_refcount_increase_nodebug(refp); \ 160 | } else { \ 161 | imquic_refcount_increase_debug(refp); \ 162 | } \ 163 | } 164 | /*! \brief Increase the imquic reference counter (no debug) 165 | * @param refp Pointer to the imquic reference counter instance */ 166 | #define imquic_refcount_increase_nodebug(refp) { \ 167 | g_atomic_int_inc((gint *)&(refp)->count); \ 168 | } 169 | /*! \brief Increase the imquic reference counter (debug) 170 | * @param refp Pointer to the imquic reference counter instance */ 171 | #define imquic_refcount_increase_debug(refp) { \ 172 | IMQUIC_PRINT("[%s:%s:%d:increase] %p (%d)\n", __FILE__, __FUNCTION__, __LINE__, refp, (refp)->count+1); \ 173 | g_atomic_int_inc((gint *)&(refp)->count); \ 174 | } 175 | 176 | /*! \brief Decrease the imquic reference counter (debug according to settings) 177 | * \note Will invoke the \c free function if the counter reaches 0 178 | * @param refp Pointer to the imquic reference counter instance */ 179 | #define imquic_refcount_decrease(refp) { \ 180 | if(!imquic_refcount_debug) { \ 181 | imquic_refcount_decrease_nodebug(refp); \ 182 | } else { \ 183 | imquic_refcount_decrease_debug(refp); \ 184 | } \ 185 | } 186 | /*! \brief Decrease the imquic reference counter (debug) 187 | * \note Will invoke the \c free function if the counter reaches 0 188 | * @param refp Pointer to the imquic reference counter instance */ 189 | #ifdef IMQUIC_REFCOUNT_DEBUG 190 | #define imquic_refcount_decrease_debug(refp) { \ 191 | IMQUIC_PRINT("[%s:%s:%d:decrease] %p (%d)\n", __FILE__, __FUNCTION__, __LINE__, refp, (refp)->count-1); \ 192 | if(g_atomic_int_dec_and_test((gint *)&(refp)->count)) { \ 193 | (refp)->free(refp); \ 194 | imquic_refcount_untrack((refp)); \ 195 | } \ 196 | } 197 | #else 198 | #define imquic_refcount_decrease_debug(refp) { \ 199 | IMQUIC_PRINT("[%s:%s:%d:decrease] %p (%d)\n", __FILE__, __FUNCTION__, __LINE__, refp, (refp)->count-1); \ 200 | if(g_atomic_int_dec_and_test((gint *)&(refp)->count)) { \ 201 | (refp)->free(refp); \ 202 | } \ 203 | } 204 | #endif 205 | /*! \brief Decrease the imquic reference counter (no debug) 206 | * \note Will invoke the \c free function if the counter reaches 0 207 | * @param refp Pointer to the imquic reference counter instance */ 208 | #ifdef IMQUIC_REFCOUNT_DEBUG 209 | #define imquic_refcount_decrease_nodebug(refp) { \ 210 | if(g_atomic_int_dec_and_test((gint *)&(refp)->count)) { \ 211 | (refp)->free(refp); \ 212 | imquic_refcount_untrack((refp)); \ 213 | } \ 214 | } 215 | #else 216 | #define imquic_refcount_decrease_nodebug(refp) { \ 217 | if(g_atomic_int_dec_and_test((gint *)&(refp)->count)) { \ 218 | (refp)->free(refp); \ 219 | } \ 220 | } 221 | #endif 222 | 223 | #endif 224 | -------------------------------------------------------------------------------- /src/internal/roq.h: -------------------------------------------------------------------------------- 1 | /*! \file roq.h 2 | * \author Lorenzo Miniero 3 | * \copyright MIT License 4 | * \brief RTP Over QUIC (RoQ) stack (headers) 5 | * \details Implementation of the RTP Over QUIC (RoQ) stack as part 6 | * of the library itself. At the time of writing, this implements (most 7 | * of) version -10 of the protocol. 8 | * 9 | * \note This is the internal implementation of RoQ in the library. You're 10 | * still free to only use imquic as the underlying QUIC/WebTransport library, 11 | * and take care of the RoQ implementation on your own instead: in order 12 | * to do that, use the generic imquic client/server creation utilities, 13 | * rather than the RoQ specific ones. 14 | * 15 | * \ingroup RoQ Core 16 | */ 17 | 18 | #ifndef IMQUIC_ROQ_INTERNAL_H 19 | #define IMQUIC_ROQ_INTERNAL_H 20 | 21 | #include 22 | 23 | #include "../imquic/imquic.h" 24 | #include "qlog.h" 25 | #include "mutex.h" 26 | #include "refcount.h" 27 | 28 | #define IMQUIC_ROQ 7499633 29 | 30 | /*! \brief Initialize the native RoQ stack at startup */ 31 | void imquic_roq_init(void); 32 | /*! \brief Uninitialize the native RoQ stack */ 33 | void imquic_roq_deinit(void); 34 | 35 | /*! \brief RoQ endpoint instance */ 36 | typedef struct imquic_roq_endpoint { 37 | /*! \brief Associated QUIC connection */ 38 | imquic_connection *conn; 39 | /*! \brief Stream flows, indexed by stream ID */ 40 | GHashTable *stream_flows_in, *stream_flows_out; 41 | /*! \brief Current packet buffer, indexed by stream ID */ 42 | GHashTable *packets; 43 | /*! \brief Mutex */ 44 | imquic_mutex mutex; 45 | } imquic_roq_endpoint; 46 | 47 | /*! \brief RoQ stream (when using the same stream for multiple packets) */ 48 | typedef struct imquic_roq_stream { 49 | /*! \brief QUIC Stream ID */ 50 | uint64_t stream_id; 51 | /*! \brief RoQ Flow ID */ 52 | uint64_t flow_id; 53 | /*! \brief Offset in the QUIC STREAM where to write the next packet */ 54 | uint64_t offset; 55 | /*! \brief Whether this instance has been destroyed (reference counting) */ 56 | volatile gint destroyed; 57 | /*! \brief Reference counter */ 58 | imquic_refcount ref; 59 | } imquic_roq_stream; 60 | 61 | /*! \brief RoQ public callbacks */ 62 | typedef struct imquic_roq_callbacks { 63 | /*! \brief Callback function to be notified about new RoQ connections */ 64 | void (* new_connection)(imquic_connection *conn, void *user_data); 65 | /*! \brief Callback function to be notified about incoming RTP packets */ 66 | void (* rtp_incoming)(imquic_connection *conn, uint64_t flow_id, uint8_t *bytes, size_t blen); 67 | /*! \brief Callback function to be notified about RoQ connections being closed */ 68 | void (* connection_gone)(imquic_connection *conn); 69 | } imquic_roq_callbacks; 70 | 71 | /** @name Internal callbacks for RoQ endpoints 72 | */ 73 | ///@{ 74 | /*! \brief Callback the core invokes when a new QUIC connection using RoQ is available 75 | * @param conn The imquic_connection instance that is now available 76 | * @param user_data Optional user data the user/application may have 77 | * associated to the endpoint this connection belongs to */ 78 | void imquic_roq_new_connection(imquic_connection *conn, void *user_data); 79 | /*! \brief Callback the core invokes when there's new incoming data on a \c STREAM 80 | * @param conn The imquic_connection instance for which new \c STREAM data is available 81 | * @param stream_id The QUIC Stream ID for which new data is available 82 | * @param bytes The new data that is available 83 | * @param offset The offset in the stream this new data should be put in 84 | * @param length Size of the new data 85 | * @param complete Whether this data marks the end of this \c STREAM */ 86 | void imquic_roq_stream_incoming(imquic_connection *conn, uint64_t stream_id, 87 | uint8_t *bytes, uint64_t offset, uint64_t length, gboolean complete); 88 | /*! \brief Callback the core invokes when there's new incoming data on a \c DATAGRAM 89 | * @param conn The imquic_connection instance for which new \c DATAGRAM data is available 90 | * @param bytes The new data that is available 91 | * @param length Size of the new data */ 92 | void imquic_roq_datagram_incoming(imquic_connection *conn, uint8_t *bytes, uint64_t length); 93 | /*! \brief Callback the core invokes when an existing RoQ connection is not available anymore 94 | * @param conn The imquic_connection instance that is now gone */ 95 | void imquic_roq_connection_gone(imquic_connection *conn); 96 | ///@} 97 | 98 | #ifdef HAVE_QLOG 99 | /** @name QLOG events tracing for RoQ 100 | */ 101 | ///@{ 102 | /*! \brief Helper to add fields for RtpPacket to an event 103 | * @param data The data object to add the properties to 104 | * @param flow_id The RoQ flow ID to add 105 | * @param length The length of the RTP packet */ 106 | void imquic_roq_qlog_add_rtp_packet(json_t *data, uint64_t flow_id, uint64_t length); 107 | /*! \brief Add a \c stream_opened event 108 | * @param qlog The imquic_qlog instance to add the event to 109 | * @param stream_id The Stream ID that was opened 110 | * @param flow_id The RoQ flow ID used in the stream */ 111 | void imquic_roq_qlog_stream_opened(imquic_qlog *qlog, uint64_t stream_id, uint64_t flow_id); 112 | /*! \brief Add a \c stream_packet_created event 113 | * @param qlog The imquic_qlog instance to add the event to 114 | * @param stream_id The Stream ID used for the packet 115 | * @param flow_id The RoQ flow ID used in the stream 116 | * @param length The length of the RTP packet */ 117 | void imquic_roq_qlog_stream_packet_created(imquic_qlog *qlog, uint64_t stream_id, uint64_t flow_id, uint64_t length); 118 | /*! \brief Add a \c stream_packet_parsed event 119 | * @param qlog The imquic_qlog instance to add the event to 120 | * @param stream_id The Stream ID used for the packet 121 | * @param flow_id The RoQ flow ID used in the stream 122 | * @param length The length of the RTP packet */ 123 | void imquic_roq_qlog_stream_packet_parsed(imquic_qlog *qlog, uint64_t stream_id, uint64_t flow_id, uint64_t length); 124 | /*! \brief Add a \c datagram_packet_created event 125 | * @param qlog The imquic_qlog instance to add the event to 126 | * @param flow_id The RoQ flow ID used in the datagram 127 | * @param length The length of the RTP packet */ 128 | void imquic_roq_qlog_datagram_packet_created(imquic_qlog *qlog, uint64_t flow_id, uint64_t length); 129 | /*! \brief Add a \c datagram_packet_parsed event 130 | * @param qlog The imquic_qlog instance to add the event to 131 | * @param flow_id The RoQ flow ID used in the datagram 132 | * @param length The length of the RTP packet */ 133 | void imquic_roq_qlog_datagram_packet_parsed(imquic_qlog *qlog, uint64_t flow_id, uint64_t length); 134 | ///@} 135 | #endif 136 | 137 | #endif 138 | -------------------------------------------------------------------------------- /src/internal/stream.h: -------------------------------------------------------------------------------- 1 | /*! \file stream.h 2 | * \author Lorenzo Miniero 3 | * \copyright MIT License 4 | * \brief QUIC STREAM abstraction (headers) 5 | * \details Abstraction of QUIC STREAMs, to facilitate the management 6 | * of client/server unidirectional/bidirectional streams created within 7 | * the contect of a QUIC connection. 8 | * 9 | * \ingroup Core 10 | */ 11 | 12 | #ifndef IMQUIC_STREAM_H 13 | #define IMQUIC_STREAM_H 14 | 15 | #include 16 | 17 | #include 18 | 19 | #include "buffer.h" 20 | #include "refcount.h" 21 | 22 | /*! \brief Stream states */ 23 | typedef enum imquic_stream_state { 24 | /*! \brief Inactive */ 25 | IMQUIC_STREAM_INACTIVE, 26 | /*! \brief Ready */ 27 | IMQUIC_STREAM_READY, 28 | /*! \brief Blocked */ 29 | IMQUIC_STREAM_BLOCKED, 30 | /*! \brief Reset */ 31 | IMQUIC_STREAM_RESET, 32 | /*! \brief Complete */ 33 | IMQUIC_STREAM_COMPLETE 34 | } imquic_stream_state; 35 | /*! \brief Helper function to serialize to string the name of a imquic_stream_state value. 36 | * @param state The imquic_stream_state value 37 | * @returns The state name as a string, if valid, or NULL otherwise */ 38 | const char *imquic_stream_state_str(imquic_stream_state state); 39 | 40 | /*! \brief QUIC stream */ 41 | typedef struct imquic_stream { 42 | /*! \brief Stream ID and actual ID */ 43 | uint64_t stream_id, actual_id; 44 | /*! \brief Whether the stream is client or server originated, and bidirectional or unidirectional */ 45 | gboolean client_initiated, bidirectional; 46 | /*! \brief Whether the stream can send and receive data */ 47 | gboolean can_send, can_receive; 48 | /*! \brief Size of stream incoming and outgoing data so far (for flow control) */ 49 | uint64_t in_size, out_size; 50 | /*! \brief Stream incoming and outgoing final size (for flow control) */ 51 | uint64_t in_finalsize, out_finalsize; 52 | /*! \brief Stream incoming and outgoing state */ 53 | imquic_stream_state in_state, out_state; 54 | /*! \brief Number of bytes to skip, when dealing with offsets (e.g., to hide 55 | * the shifted offsets when a protocol is encapsulated on a WebTransport */ 56 | size_t skip_in, skip_out; 57 | /*! \brief Incoming and outgoing buffers */ 58 | imquic_buffer *in_data, *out_data; 59 | /*! \brief Flow control state for this stream */ 60 | uint64_t local_max_data, remote_max_data; 61 | /*! \brief Mutex */ 62 | imquic_mutex mutex; 63 | /*! \brief Whether this instance has been destroyed (reference counting) */ 64 | volatile gint destroyed; 65 | /*! \brief Reference counter */ 66 | imquic_refcount ref; 67 | } imquic_stream; 68 | /*! \brief Helper method to create a new stream 69 | * @param stream_id The stream ID 70 | * @param is_server Whether the endpoint this stream is added to is a server 71 | * @returns A pointer to a new imquic_stream instance, if successful, or NULL otherwise */ 72 | imquic_stream *imquic_stream_create(uint64_t stream_id, gboolean is_server); 73 | /*! \brief Helper method to check whether an endpoint can send data on this stream 74 | * @note This checks characteristics of the stream (e.g., client-originated, 75 | * bidirectional, etc.), the stream state, and whether a stream is complete 76 | * @param stream The imquic_stream instance to check 77 | * @param offset Offset in the stream from where the new data would be sent 78 | * @param length Length of the data that would be sent 79 | * @param verbose Whether details on the checks should be logged in a verbose way with warnings 80 | * @returns TRUE if data can be sent, FALSE otherwise */ 81 | gboolean imquic_stream_can_send(imquic_stream *stream, uint64_t offset, uint64_t length, gboolean verbose); 82 | /*! \brief Helper method to check whether an endpoint can receive data on this stream 83 | * @note This checks characteristics of the stream (e.g., client-originated, 84 | * bidirectional, etc.), the stream state, and whether a stream is complete 85 | * @param stream The imquic_stream instance to check 86 | * @param offset Offset in the stream from where the new data would be received 87 | * @param length Length of the data that would be received 88 | * @param verbose Whether details on the checks should be logged in a verbose way with warnings 89 | * @returns TRUE if data can be received, FALSE otherwise */ 90 | gboolean imquic_stream_can_receive(imquic_stream *stream, uint64_t offset, uint64_t length, gboolean verbose); 91 | /*! \brief Helper method to mark a stream as complete in one direction 92 | * @note This may end up marking the stream as complete in general, 93 | * depending on the stream state or the unidirectional nature of the stream 94 | * @param stream The imquic_stream instance to update 95 | * @param incoming Whether the stream is now complete on the way in or on the way out 96 | * @returns TRUE in case of success, or FALSE otherwise */ 97 | gboolean imquic_stream_mark_complete(imquic_stream *stream, gboolean incoming); 98 | /*! \brief Helper method to check whether an existing stream is now done 99 | * @param stream The imquic_stream instance to check 100 | * @returns TRUE if the stream is now done, FALSE otherwise */ 101 | gboolean imquic_stream_is_done(imquic_stream *stream); 102 | /*! \brief Helper method to destroy an existing imquic_stream instance 103 | * @param stream The imquic_stream instance to destroy */ 104 | void imquic_stream_destroy(imquic_stream *stream); 105 | 106 | /** @name Stream utilities 107 | */ 108 | ///@{ 109 | /*! \brief Parse a QUIC stream ID to its actual ID and its other properties 110 | * @param[in] stream_id The QUIC stream ID to parse 111 | * @param[out] id The actual client/server uni/bidirectional ID 112 | * @param[out] client_initiated Whether this stream is client initiated 113 | * @param[out] bidirectional Whether this stream is bidirectional */ 114 | void imquic_parse_stream_id(uint64_t stream_id, uint64_t *id, gboolean *client_initiated, gboolean *bidirectional); 115 | /*! \brief Build a QUIC stream ID out of its actual ID and its other properties 116 | * @param[in] id The actual client/server uni/bidirectional ID 117 | * @param[in] client_initiated Whether this stream is client initiated 118 | * @param[in] bidirectional Whether this stream is bidirectional 119 | * @returns The QUIC stream ID */ 120 | uint64_t imquic_build_stream_id(uint64_t id, gboolean client_initiated, gboolean bidirectional); 121 | ///@} 122 | 123 | #endif 124 | -------------------------------------------------------------------------------- /src/internal/version.h: -------------------------------------------------------------------------------- 1 | /*! \file version.h 2 | * \author Lorenzo Miniero 3 | * \copyright MIT License 4 | * \brief imquic versioning (headers) 5 | * \details This exposes a quick an easy way to display the commit the 6 | * compiled version of imquic implements, and when it has been built. It 7 | * is based on this excellent comment: http://stackoverflow.com/a/1843783 8 | * 9 | * \ingroup API Core 10 | */ 11 | 12 | #ifndef IMQUIC_VERSION_H 13 | #define IMQUIC_VERSION_H 14 | 15 | extern const char *imquic_name; 16 | extern int imquic_version_major; 17 | extern int imquic_version_minor; 18 | extern int imquic_version_patch; 19 | extern const char *imquic_version_release; 20 | extern const char *imquic_version_string; 21 | extern const char *imquic_version_string_full; 22 | extern const char *imquic_build_git_time; 23 | extern const char *imquic_build_git_sha; 24 | 25 | #endif 26 | -------------------------------------------------------------------------------- /src/listmap.c: -------------------------------------------------------------------------------- 1 | /*! \file listmap.c 2 | * \author Lorenzo Miniero 3 | * \copyright MIT License 4 | * \brief Combined list and map utility 5 | * \details Implementation of a generic structure that contains properties 6 | * both of a map and a linked list (with quick pointers to the head to 7 | * work a bit like a queue, when appending). Mostly to be used for tracking 8 | * in an efficient way resources that must be ordered, but also quickly 9 | * accessed via a key (e.g., sent packets by packet number). 10 | * 11 | * \ingroup Core 12 | */ 13 | 14 | #include 15 | 16 | #include "internal/listmap.h" 17 | #include "internal/utils.h" 18 | #include "imquic/debug.h" 19 | 20 | /* Create a new listmap */ 21 | imquic_listmap *imquic_listmap_create(imquic_listmap_key type, GDestroyNotify destroy) { 22 | imquic_listmap *lm = g_malloc(sizeof(imquic_listmap)); 23 | lm->type = type; 24 | lm->list = lm->index = lm->last = NULL; 25 | if(type == IMQUIC_LISTMAP_NUMBER) { 26 | lm->table = g_hash_table_new_full(NULL, NULL, NULL, destroy); 27 | } else if(type == IMQUIC_LISTMAP_STRING) { 28 | lm->table = g_hash_table_new_full(g_str_hash, g_str_equal, (GDestroyNotify)g_free, destroy); 29 | } else if(type == IMQUIC_LISTMAP_NUMBER64) { 30 | lm->table = g_hash_table_new_full(g_int64_hash, g_int64_equal, (GDestroyNotify)g_free, destroy); 31 | } else { 32 | IMQUIC_LOG(IMQUIC_LOG_ERR, "Invalid listmap key type\n"); 33 | g_free(lm); 34 | return NULL; 35 | } 36 | lm->length = 0; 37 | lm->destroy = destroy; 38 | return lm; 39 | } 40 | 41 | /* Destroy a listmap */ 42 | void imquic_listmap_destroy(imquic_listmap *lm) { 43 | if(lm != NULL) { 44 | if(lm->list != NULL) 45 | g_list_free(lm->list); 46 | if(lm->table != NULL) 47 | g_hash_table_unref(lm->table); 48 | g_free(lm); 49 | } 50 | } 51 | 52 | /* Adding items */ 53 | static void imquic_listmap_add_to_table(imquic_listmap *lm, void *key, void *item) { 54 | if(lm != NULL) { 55 | if(lm->type == IMQUIC_LISTMAP_NUMBER) { 56 | g_hash_table_insert(lm->table, key, item); 57 | } else if(lm->type == IMQUIC_LISTMAP_STRING && key != NULL) { 58 | g_hash_table_insert(lm->table, g_strdup((char *)key), item); 59 | } else if(lm->type == IMQUIC_LISTMAP_NUMBER64) { 60 | uint64_t *num = (uint64_t *)key; 61 | g_hash_table_insert(lm->table, imquic_dup_uint64(*num), item); 62 | } 63 | } 64 | } 65 | 66 | int imquic_listmap_prepend(imquic_listmap *lm, void *key, void *item) { 67 | if(lm == NULL || (lm->type == IMQUIC_LISTMAP_STRING && key == NULL)) 68 | return -1; 69 | /* If the key exists already, get rid of that first */ 70 | if(imquic_listmap_find(lm, key)) 71 | imquic_listmap_remove(lm, key); 72 | /* Prepend in the list */ 73 | lm->list = g_list_prepend(lm->list, item); 74 | if(lm->last == NULL) 75 | lm->last = lm->list; 76 | lm->index = NULL; 77 | /* Add to the map */ 78 | imquic_listmap_add_to_table(lm, key, item); 79 | lm->length++; 80 | return 0; 81 | } 82 | 83 | int imquic_listmap_append(imquic_listmap *lm, void *key, void *item) { 84 | if(lm == NULL || (lm->type == IMQUIC_LISTMAP_STRING && key == NULL)) 85 | return -1; 86 | /* If the key exists already, get rid of that first */ 87 | if(imquic_listmap_find(lm, key)) 88 | imquic_listmap_remove(lm, key); 89 | /* Append to the list */ 90 | lm->last = g_list_append(lm->last, item); 91 | if(lm->list == NULL) 92 | lm->list = lm->last; 93 | if(lm->last->next != NULL) 94 | lm->last = lm->last->next; 95 | lm->index = NULL; 96 | /* Add to the map */ 97 | imquic_listmap_add_to_table(lm, key, item); 98 | lm->length++; 99 | return 0; 100 | } 101 | 102 | /* Removing items */ 103 | int imquic_listmap_remove(imquic_listmap *lm, void *key) { 104 | if(lm == NULL || (lm->type == IMQUIC_LISTMAP_STRING && key == NULL)) 105 | return -1; 106 | void *item = g_hash_table_lookup(lm->table, key); 107 | if(item != NULL) { 108 | lm->list = g_list_remove(lm->list, item); 109 | /* FIXME Is there a more efficient way to update the tail? */ 110 | lm->last = g_list_last(lm->list); 111 | g_hash_table_remove(lm->table, key); 112 | lm->length--; 113 | } 114 | return 0; 115 | } 116 | 117 | int imquic_listmap_clear(imquic_listmap *lm) { 118 | if(lm == NULL) 119 | return -1; 120 | g_hash_table_remove_all(lm->table); 121 | g_list_free(lm->list); 122 | lm->list = NULL; 123 | lm->last = NULL; 124 | lm->index = NULL; 125 | lm->length = 0; 126 | return 0; 127 | } 128 | 129 | /* Finding items */ 130 | void *imquic_listmap_find(imquic_listmap *lm, void *key) { 131 | if(lm == NULL || (lm->type == IMQUIC_LISTMAP_STRING && key == NULL)) 132 | return NULL; 133 | return g_hash_table_lookup(lm->table, key); 134 | } 135 | 136 | gboolean imquic_listmap_contains(imquic_listmap *lm, void *item) { 137 | if(lm == NULL || lm->list == NULL) 138 | return FALSE; 139 | return g_list_find(lm->list, item) != NULL; 140 | } 141 | 142 | /* Traversing the list */ 143 | void imquic_listmap_traverse(imquic_listmap *lm) { 144 | if(lm != NULL) 145 | lm->index = NULL; 146 | } 147 | 148 | void *imquic_listmap_next(imquic_listmap *lm, gboolean *found) { 149 | if(found) 150 | *found = FALSE; 151 | if(lm == NULL) 152 | return NULL; 153 | lm->index = lm->index ? lm->index->next : lm->list; 154 | if(lm->index != NULL) { 155 | /* Found */ 156 | if(found) 157 | *found = TRUE; 158 | return lm->index->data; 159 | } 160 | /* We reached the end of the list */ 161 | return NULL; 162 | } 163 | 164 | void *imquic_listmap_prev(imquic_listmap *lm, gboolean *found) { 165 | if(found) 166 | *found = FALSE; 167 | if(lm == NULL || lm->index == NULL) 168 | return NULL; 169 | lm->index = lm->index->prev; 170 | if(lm->index != NULL) { 171 | /* Found */ 172 | if(found) 173 | *found = TRUE; 174 | return lm->index->data; 175 | } 176 | /* We reached the beginning of the list */ 177 | return NULL; 178 | } 179 | -------------------------------------------------------------------------------- /src/loop.c: -------------------------------------------------------------------------------- 1 | /*! \file loop.c 2 | * \author Lorenzo Miniero 3 | * \copyright MIT License 4 | * \brief Event loop 5 | * \details Implementation of an event loop to be used in the imquic 6 | * library internals, mostly for networking and the dispatching of some 7 | * events. 8 | * 9 | * \todo At the moment, a single event loop is created a startup that 10 | * all connections share. Besides, the events that can be dispatched 11 | * are defined in a suboptimal way that will need to be fixed. 12 | * 13 | * \ingroup Core 14 | */ 15 | 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | 24 | #include "internal/loop.h" 25 | #include "internal/network.h" 26 | #include "internal/quic.h" 27 | #include "imquic/debug.h" 28 | 29 | /* Resources */ 30 | static GMainContext *ctx = NULL; 31 | static GMainLoop *loop = NULL; 32 | static GThread *thread = NULL; 33 | static volatile int loop_started = 0; 34 | static void *imquic_loop_thread(void *data) { 35 | IMQUIC_LOG(IMQUIC_LOG_VERB, "Joining event loop thread...\n"); 36 | g_atomic_int_set(&loop_started, 1); 37 | /* Run the main loop */ 38 | g_main_loop_run(loop); 39 | /* When the loop ends, we're done */ 40 | IMQUIC_LOG(IMQUIC_LOG_VERB, "Leaving event loop thread...\n"); 41 | return NULL; 42 | } 43 | 44 | /* Network source */ 45 | typedef struct imquic_network_source { 46 | imquic_source parent; 47 | imquic_network_endpoint *ne; 48 | GDestroyNotify destroy; 49 | } imquic_network_source; 50 | static void imquic_network_endpoint_receive(imquic_network_endpoint *ne) { 51 | if(ne == NULL || ne->fd == -1) 52 | return; 53 | char buffer[4906]; 54 | imquic_network_address sender = { 0 }; 55 | sender.addrlen = sizeof(sender.addr); 56 | int len = recvfrom(ne->fd, buffer, sizeof(buffer), 0, (struct sockaddr *)&sender.addr, &sender.addrlen); 57 | if(len > 0) { 58 | /* Invoke the callback function for parsing the QUIC message */ 59 | imquic_process_message(ne, &sender, (uint8_t *)buffer, (size_t)len); 60 | } 61 | } 62 | static gboolean imquic_network_source_prepare(GSource *source, gint *timeout) { 63 | *timeout = -1; 64 | return FALSE; 65 | } 66 | static gboolean imquic_network_source_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) { 67 | imquic_network_source *ns = (imquic_network_source *)source; 68 | /* Receive the packet */ 69 | imquic_network_endpoint_receive(ns->ne); 70 | return G_SOURCE_CONTINUE; 71 | } 72 | static void imquic_network_source_finalize(GSource *source) { 73 | imquic_network_source *ns = (imquic_network_source *)source; 74 | if(ns && ns->ne) { 75 | if(ns->ne->source) 76 | ns->ne->source = NULL; 77 | imquic_network_endpoint_destroy(ns->ne); 78 | ns->ne = NULL; 79 | } 80 | } 81 | static GSourceFuncs imquic_network_source_funcs = { 82 | imquic_network_source_prepare, 83 | NULL, 84 | imquic_network_source_dispatch, 85 | imquic_network_source_finalize, 86 | NULL, NULL 87 | }; 88 | 89 | /* Connection events */ 90 | typedef struct imquic_connection_source { 91 | imquic_source parent; 92 | imquic_connection *conn; 93 | GDestroyNotify destroy; 94 | } imquic_connection_source; 95 | static gboolean imquic_connection_source_prepare(GSource *source, gint *timeout) { 96 | imquic_connection_source *cs = (imquic_connection_source *)source; 97 | return g_atomic_int_get(&cs->conn->wakeup); 98 | } 99 | static gboolean imquic_connection_source_dispatch(GSource *source, GSourceFunc callback, gpointer user_data) { 100 | imquic_connection_source *cs = (imquic_connection_source *)source; 101 | return imquic_handle_event(cs->conn); 102 | } 103 | static void imquic_connection_source_finalize(GSource *source) { 104 | imquic_connection_source *cs = (imquic_connection_source *)source; 105 | imquic_refcount_decrease(&cs->conn->ref); 106 | } 107 | static GSourceFuncs imquic_connection_source_funcs = { 108 | imquic_connection_source_prepare, 109 | NULL, /* We don't need check */ 110 | imquic_connection_source_dispatch, 111 | imquic_connection_source_finalize, 112 | NULL, NULL 113 | }; 114 | 115 | /* Initialize the event loop */ 116 | int imquic_loop_init(void) { 117 | /* Initialize main context and loop */ 118 | ctx = g_main_context_new(); 119 | loop = g_main_loop_new(ctx, FALSE); 120 | /* Start the thread that will handle the loop */ 121 | GError *error = NULL; 122 | thread = g_thread_try_new("imquic-loop", imquic_loop_thread, NULL, &error); 123 | if(error != NULL) { 124 | /* We show the error but it's not fatal */ 125 | IMQUIC_LOG(IMQUIC_LOG_ERR, "Got error %d (%s) trying to launch the event loop...\n", 126 | error->code, error->message ? error->message : "??"); 127 | g_error_free(error); 128 | return -1; 129 | } 130 | while(!g_atomic_int_get(&loop_started)) 131 | g_usleep(5000); 132 | /* Done */ 133 | return 0; 134 | } 135 | 136 | void imquic_loop_wakeup(void) { 137 | g_main_context_wakeup(ctx); 138 | } 139 | 140 | void imquic_loop_deinit(void) { 141 | if(loop != NULL) 142 | g_main_loop_quit(loop); 143 | if(thread != NULL) 144 | g_thread_join(thread); 145 | } 146 | 147 | /* Helpers to add events to the loop */ 148 | imquic_source *imquic_loop_poll_endpoint(void *e) { 149 | imquic_network_source *ns = (imquic_network_source *)g_source_new(&imquic_network_source_funcs, sizeof(imquic_network_source)); 150 | imquic_network_endpoint *ne = (imquic_network_endpoint *)e; 151 | ns->ne = ne; 152 | ne->source = ns; 153 | g_source_set_priority((GSource *)ns, G_PRIORITY_DEFAULT); 154 | g_source_add_unix_fd((GSource *)ns, ne->fd, G_IO_IN | G_IO_ERR); 155 | g_source_attach((GSource *)ns, ctx); 156 | return (imquic_source *)ns; 157 | } 158 | 159 | imquic_source *imquic_loop_poll_connection(void *c) { 160 | imquic_connection_source *cs = (imquic_connection_source *)g_source_new(&imquic_connection_source_funcs, sizeof(imquic_connection_source)); 161 | imquic_connection *conn = (imquic_connection *)c; 162 | char name[255], temp[41]; 163 | const char *alpn = conn->socket ? conn->socket->alpn : "??"; 164 | g_snprintf(name, sizeof(name), "%s-%s", alpn, imquic_connection_id_str(&conn->local_cid, temp, sizeof(temp))); 165 | g_source_set_name((GSource *)cs, name); 166 | cs->conn = conn; 167 | imquic_refcount_increase(&cs->conn->ref); 168 | g_source_set_priority((GSource *)cs, G_PRIORITY_DEFAULT); 169 | g_source_attach((GSource *)cs, ctx); 170 | return (imquic_source *)cs; 171 | } 172 | 173 | imquic_source *imquic_loop_add_timer(guint ms, GSourceFunc func, gpointer data) { 174 | imquic_source *timer = (imquic_source *)g_timeout_source_new(ms); 175 | g_source_set_callback((GSource *)timer, func, data, NULL); 176 | g_source_attach((GSource *)timer, ctx); 177 | g_source_unref((GSource *)timer); 178 | return timer; 179 | } 180 | -------------------------------------------------------------------------------- /src/stream.c: -------------------------------------------------------------------------------- 1 | /*! \file stream.c 2 | * \author Lorenzo Miniero 3 | * \copyright MIT License 4 | * \brief QUIC STREAM abstraction 5 | * \details Abstraction of QUIC STREAMs, to facilitate the management 6 | * of client/server unidirectional/bidirectional streams created within 7 | * the contect of a QUIC connection. 8 | * 9 | * \ingroup Core 10 | */ 11 | 12 | #include "internal/connection.h" 13 | #include "internal/stream.h" 14 | #include "imquic/debug.h" 15 | 16 | const char *imquic_stream_state_str(imquic_stream_state state) { 17 | switch(state) { 18 | case IMQUIC_STREAM_INACTIVE: 19 | return "inactive"; 20 | case IMQUIC_STREAM_READY: 21 | return "ready"; 22 | case IMQUIC_STREAM_BLOCKED: 23 | return "blocked"; 24 | case IMQUIC_STREAM_RESET: 25 | return "reset"; 26 | case IMQUIC_STREAM_COMPLETE: 27 | return "complete"; 28 | default: break; 29 | } 30 | return NULL; 31 | } 32 | 33 | /* Stream initialization */ 34 | static void imquic_stream_free(const imquic_refcount *stream_ref) { 35 | imquic_stream *stream = imquic_refcount_containerof(stream_ref, imquic_stream, ref); 36 | imquic_buffer_destroy(stream->in_data); 37 | imquic_buffer_destroy(stream->out_data); 38 | g_free(stream); 39 | } 40 | 41 | imquic_stream *imquic_stream_create(uint64_t stream_id, gboolean is_server) { 42 | imquic_stream *stream = g_malloc0(sizeof(imquic_stream)); 43 | stream->stream_id = stream_id; 44 | imquic_parse_stream_id(stream_id, &stream->actual_id, &stream->client_initiated, &stream->bidirectional); 45 | IMQUIC_LOG(IMQUIC_LOG_HUGE, "New stream: %"SCNu64" (%"SCNu64", %s initiated, %s)\n", 46 | stream_id, stream->actual_id, 47 | stream->client_initiated ? "client" : "server", 48 | stream->bidirectional ? "bidirectional" : "unidirectional"); 49 | stream->can_send = (is_server == !stream->client_initiated) || stream->bidirectional; 50 | stream->can_receive = (is_server == stream->client_initiated) || stream->bidirectional; 51 | stream->in_state = stream->can_receive ? IMQUIC_STREAM_READY : IMQUIC_STREAM_INACTIVE; 52 | stream->out_state = stream->can_send ? IMQUIC_STREAM_READY : IMQUIC_STREAM_INACTIVE; 53 | stream->in_data = stream->can_receive ? imquic_buffer_create(stream_id) : NULL; 54 | stream->out_data = stream->can_send ? imquic_buffer_create(stream_id) : NULL; 55 | imquic_mutex_init(&stream->mutex); 56 | imquic_refcount_init(&stream->ref, imquic_stream_free); 57 | return stream; 58 | } 59 | 60 | gboolean imquic_stream_can_send(imquic_stream *stream, uint64_t offset, uint64_t length, gboolean verbose) { 61 | if(stream == NULL) 62 | return FALSE; 63 | if(!stream->can_send || stream->out_state == IMQUIC_STREAM_INACTIVE || stream->out_data == NULL) { 64 | if(verbose) 65 | IMQUIC_LOG(IMQUIC_LOG_WARN, "Can't send data, stream %"SCNu64" is unidirectional\n", stream->stream_id); 66 | return FALSE; 67 | } 68 | if(stream->out_state == IMQUIC_STREAM_COMPLETE || stream->in_state == IMQUIC_STREAM_RESET || stream->out_state == IMQUIC_STREAM_RESET) { 69 | if(verbose) 70 | IMQUIC_LOG(IMQUIC_LOG_WARN, "Can't send data, stream %"SCNu64" is complete/reset\n", stream->stream_id); 71 | return FALSE; 72 | } 73 | if(stream->out_finalsize > 0 && (offset + length) > stream->out_finalsize) { 74 | if(verbose) 75 | IMQUIC_LOG(IMQUIC_LOG_WARN, "Can't send data, final size for stream %"SCNu64" is known and this goes beyond it\n", stream->stream_id); 76 | return FALSE; 77 | } 78 | return TRUE; 79 | } 80 | 81 | gboolean imquic_stream_can_receive(imquic_stream *stream, uint64_t offset, uint64_t length, gboolean verbose) { 82 | if(stream == NULL) 83 | return FALSE; 84 | if(!stream->can_receive || stream->in_state == IMQUIC_STREAM_INACTIVE || stream->in_data == NULL) { 85 | if(verbose) 86 | IMQUIC_LOG(IMQUIC_LOG_WARN, "Can't receive data, stream %"SCNu64" is unidirectional\n", stream->stream_id); 87 | return FALSE; 88 | } 89 | if(stream->in_state == IMQUIC_STREAM_COMPLETE || stream->in_state == IMQUIC_STREAM_RESET || stream->in_state == IMQUIC_STREAM_RESET) { 90 | if(verbose) 91 | IMQUIC_LOG(IMQUIC_LOG_WARN, "Can't receive data, stream %"SCNu64" is complete/reset\n", stream->stream_id); 92 | return FALSE; 93 | } 94 | if(stream->in_finalsize > 0 && (offset + length) > stream->in_finalsize) { 95 | if(verbose) 96 | IMQUIC_LOG(IMQUIC_LOG_WARN, "Can't receive data, final size for stream %"SCNu64" is known and this goes beyond it\n", stream->stream_id); 97 | return FALSE; 98 | } 99 | return TRUE; 100 | } 101 | 102 | gboolean imquic_stream_mark_complete(imquic_stream *stream, gboolean incoming) { 103 | if(stream == NULL) 104 | return FALSE; 105 | /* We only mark the final size here: switching the state to complete is done elsewhere */ 106 | if(incoming) { 107 | if(stream->in_data == NULL) /* Nothing to do */ 108 | return TRUE; 109 | GList *last = g_list_last(stream->in_data->chunks); 110 | imquic_buffer_chunk *last_chunk = (imquic_buffer_chunk *)(last ? last->data : NULL); 111 | stream->in_finalsize = last_chunk ? (last_chunk->offset + last_chunk->length) : stream->in_data->base_offset; 112 | IMQUIC_LOG(IMQUIC_LOG_HUGE, "Stream %"SCNu64" has a known size for incoming data\n", stream->stream_id); 113 | } else { 114 | if(stream->out_data == NULL) /* Nothing to do */ 115 | return TRUE; 116 | GList *last = g_list_last(stream->out_data->chunks); 117 | imquic_buffer_chunk *last_chunk = (imquic_buffer_chunk *)(last ? last->data : NULL); 118 | stream->out_finalsize = last_chunk ? (last_chunk->offset + last_chunk->length) : stream->out_data->base_offset; 119 | IMQUIC_LOG(IMQUIC_LOG_HUGE, "Stream %"SCNu64" has a known size for outgoing data\n", stream->stream_id); 120 | } 121 | return TRUE; 122 | } 123 | 124 | gboolean imquic_stream_is_done(imquic_stream *stream) { 125 | if(stream == NULL) 126 | return FALSE; 127 | gboolean in_done = FALSE, out_done = FALSE; 128 | if(stream->in_state == IMQUIC_STREAM_COMPLETE || stream->in_state == IMQUIC_STREAM_RESET || stream->in_state == IMQUIC_STREAM_INACTIVE) 129 | in_done = TRUE; 130 | if(stream->out_state == IMQUIC_STREAM_COMPLETE || stream->out_state == IMQUIC_STREAM_RESET || stream->out_state == IMQUIC_STREAM_INACTIVE) 131 | out_done = TRUE; 132 | return in_done && out_done; 133 | } 134 | 135 | void imquic_stream_destroy(imquic_stream *stream) { 136 | if(stream && g_atomic_int_compare_and_exchange(&stream->destroyed, 0, 1)) 137 | imquic_refcount_decrease(&stream->ref); 138 | } 139 | 140 | /* Reading and writing Stream ID */ 141 | void imquic_parse_stream_id(uint64_t stream_id, uint64_t *id, gboolean *client_initiated, gboolean *bidirectional) { 142 | uint64_t bits = stream_id & 0x00000003; 143 | *id = stream_id >> 2; 144 | *client_initiated = (bits == 0 || bits == 2); 145 | *bidirectional = (bits == 0 || bits == 1); 146 | } 147 | 148 | uint64_t imquic_build_stream_id(uint64_t id, gboolean client_initiated, gboolean bidirectional) { 149 | uint64_t stream_id = id << 2; 150 | if(!client_initiated && bidirectional) 151 | stream_id += 0x01; 152 | else if(client_initiated && !bidirectional) 153 | stream_id += 0x02; 154 | else if(!client_initiated && !bidirectional) 155 | stream_id += 0x03; 156 | return stream_id; 157 | } 158 | --------------------------------------------------------------------------------