Adds a new demo blocking QUIC server for use with the existing demo QUIC clients

Remove util/quicserver.c

Reviewed-by: Saša Nedvědický <sashan@openssl.org>
Reviewed-by: Neil Horman <nhorman@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/25465)
This commit is contained in:
Andrew Dinh 2024-09-16 03:26:47 +08:00 committed by Neil Horman
parent 3a75cf8d9c
commit dad45ea769
11 changed files with 359 additions and 340 deletions

1
.gitignore vendored
View file

@ -207,7 +207,6 @@ providers/common/include/prov/der_ml_dsa.h
/tools/c_rehash.pl
/util/shlib_wrap.sh
/util/wrap.pl
/util/quicserver
/tags
/TAGS
*.map

View file

@ -9,6 +9,7 @@ TESTS = tls-client-block \
quic-client-block \
quic-multi-stream \
tls-client-non-block \
quic-server-block \
quic-client-non-block
CFLAGS = -I../../include -g -Wall
@ -22,13 +23,14 @@ tls-server-block: tls-server-block.o
quic-client-block: quic-client-block.o
quic-multi-stream: quic-multi-stream.o
tls-client-non-block: tls-client-non-block.o
quic-server-block: quic-server-block.o
quic-client-non-block: quic-client-non-block.o
chain: chain.pem
pkey.pem:
openssl genpkey -algorithm rsa -out pkey.pem -pkeyopt rsa_keygen_bits:2048
chain.pem: pkey.pem
openssl req -x509 -new -key pkey.pem -days 36500 -subj / -out chain.pem
openssl req -x509 -new -key pkey.pem -days 36500 -subj '/CN=localhost' -out chain.pem
$(TESTS):
$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $< $(LDLIBS)

View file

@ -51,25 +51,16 @@ The tls-client-non-block demo can be run in exactly the same way. Just replace
Running the QUIC Demos
----------------------
The QUIC demos can be run in a very similar way to the TLS demos. However, a
different server implementation will need to be used.
The QUIC demos can be run in a very similar way to the TLS demos.
The OpenSSL source distribution includes a test QUIC server implementation for
use with the demos. Note that, although this server does get built when building
OpenSSL from source, it does not get installed via "make install". After
building OpenSSL from source you will find the "quicserver" utility in the
"util" sub-directory of the top of the build tree. This server utility is not
suitable for production use and exists for test purposes only. It will be
removed from a future version of OpenSSL.
While in the demos directory the QUIC server can be run like this:
While in the demos directory the quic server can be run like this:
./../util/quicserver localhost 4443 servercert.pem serverkey.pem
LD_LIBRARY_PATH=../.. ./quic-server-block 4443 ./chain.pem ./pkey.pem
The QUIC demos can then be run in the same was as the TLS demos. For example
to run the quic-client-block demo:
SSL_CERT_FILE=rootcert.pem LD_LIBRARY_PATH=../.. ./quic-client-block localhost 4443
SSL_CERT_FILE=chain.pem LD_LIBRARY_PATH=../.. ./quic-client-block localhost 4443
Notes on the quic-hq-interop demo
---------------------------------

View file

@ -9,7 +9,9 @@ PROGRAMS{noinst} = tls-client-block \
quic-multi-stream \
tls-client-non-block \
quic-client-non-block \
quic-hq-interop
quic-hq-interop \
quic-server-block \
quic-client-non-block
INCLUDE[tls-client-block]=../../include
SOURCE[tls-client-block]=tls-client-block.c
@ -27,6 +29,10 @@ INCLUDE[tls-client-non-block]=../../include
SOURCE[tls-client-non-block]=tls-client-non-block.c
DEPEND[tls-client-non-block]=../../libcrypto ../../libssl
INCLUDE[quic-server-block]=../../include
SOURCE[quic-server-block]=quic-server-block.c
DEPEND[quic-server-block]=../../libcrypto ../../libssl
INCLUDE[quic-client-non-block]=../../include
SOURCE[quic-client-non-block]=quic-client-non-block.c
DEPEND[quic-client-non-block]=../../libcrypto ../../libssl

View file

@ -243,7 +243,8 @@ int main(int argc, char *argv[])
printf("Failed to write hostname in HTTP request\n");
goto end;
}
if (!SSL_write_ex(ssl, request_end, strlen(request_end), &written)) {
if (!SSL_write_ex2(ssl, request_end, strlen(request_end),
SSL_WRITE_FLAG_CONCLUDE, &written)) {
printf("Failed to write end of HTTP request\n");
goto end;
}

View file

@ -361,7 +361,8 @@ int main(int argc, char *argv[])
printf("Failed to write hostname in HTTP request\n");
goto end; /* Cannot retry: error */
}
while (!SSL_write_ex(ssl, request_end, strlen(request_end), &written)) {
while (!SSL_write_ex2(ssl, request_end, strlen(request_end),
SSL_WRITE_FLAG_CONCLUDE, &written)) {
if (handle_io_failure(ssl, 0) == 1)
continue; /* Retry */
printf("Failed to write end of HTTP request\n");

View file

@ -0,0 +1,310 @@
/*
* Copyright 2024 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
/*
* NB: Changes to this file should also be reflected in
* doc/man7/ossl-guide-quic-server-block.pod
*/
#include <string.h>
/* Include the appropriate header file for SOCK_STREAM */
#ifdef _WIN32 /* Windows */
# include <stdarg.h>
# include <winsock2.h>
#else /* Linux/Unix */
# include <err.h>
# include <sys/socket.h>
# include <sys/select.h>
# include <netinet/in.h>
# include <unistd.h>
#endif
#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/quic.h>
#ifdef _WIN32
static const char *progname;
static void vwarnx(const char *fmt, va_list ap)
{
if (progname != NULL)
fprintf(stderr, "%s: ", progname);
vfprintf(stderr, fmt, ap);
putc('\n', stderr);
}
static void errx(int status, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vwarnx(fmt, ap);
va_end(ap);
exit(status);
}
static void warnx(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vwarnx(fmt, ap);
va_end(ap);
}
#endif
/*
* ALPN strings for TLS handshake. Only 'http/1.0' and 'hq-interop'
* are accepted.
*/
static const unsigned char alpn_ossltest[] = {
8, 'h', 't', 't', 'p', '/', '1', '.', '0',
10, 'h', 'q', '-', 'i', 'n', 't', 'e', 'r', 'o', 'p',
};
/*
* This callback validates and negotiates the desired ALPN on the server side.
*/
static int select_alpn(SSL *ssl, const unsigned char **out,
unsigned char *out_len, const unsigned char *in,
unsigned int in_len, void *arg)
{
if (SSL_select_next_proto((unsigned char **)out, out_len, alpn_ossltest,
sizeof(alpn_ossltest), in,
in_len) == OPENSSL_NPN_NEGOTIATED)
return SSL_TLSEXT_ERR_OK;
return SSL_TLSEXT_ERR_ALERT_FATAL;
}
/* Create SSL_CTX. */
static SSL_CTX *create_ctx(const char *cert_path, const char *key_path)
{
SSL_CTX *ctx;
/*
* An SSL_CTX holds shared configuration information for multiple
* subsequent per-client connections. We specifically load a QUIC
* server method here.
*/
ctx = SSL_CTX_new(OSSL_QUIC_server_method());
if (ctx == NULL)
goto err;
/*
* Load the server's certificate *chain* file (PEM format), which includes
* not only the leaf (end-entity) server certificate, but also any
* intermediate issuer-CA certificates. The leaf certificate must be the
* first certificate in the file.
*
* In advanced use-cases this can be called multiple times, once per public
* key algorithm for which the server has a corresponding certificate.
* However, the corresponding private key (see below) must be loaded first,
* *before* moving on to the next chain file.
*
* The requisite files "chain.pem" and "pkey.pem" can be generated by running
* "make chain" in this directory. If the server will be executed from some
* other directory, move or copy the files there.
*/
if (SSL_CTX_use_certificate_chain_file(ctx, cert_path) <= 0) {
fprintf(stderr, "couldn't load certificate file: %s\n", cert_path);
goto err;
}
/*
* Load the corresponding private key, this also checks that the private
* key matches the just loaded end-entity certificate. It does not check
* whether the certificate chain is valid, the certificates could be
* expired, or may otherwise fail to form a chain that a client can validate.
*/
if (SSL_CTX_use_PrivateKey_file(ctx, key_path, SSL_FILETYPE_PEM) <= 0) {
fprintf(stderr, "couldn't load key file: %s\n", key_path);
goto err;
}
/*
* Clients rarely employ certificate-based authentication, and so we don't
* require "mutual" TLS authentication (indeed there's no way to know
* whether or how the client authenticated the server, so the term "mutual"
* is potentially misleading).
*
* Since we're not soliciting or processing client certificates, we don't
* need to configure a trusted-certificate store, so no call to
* SSL_CTX_set_default_verify_paths() is needed. The server's own
* certificate chain is assumed valid.
*/
SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
/* Setup ALPN negotiation callback to decide which ALPN is accepted. */
SSL_CTX_set_alpn_select_cb(ctx, select_alpn, NULL);
return ctx;
err:
SSL_CTX_free(ctx);
return NULL;
}
/* Create UDP socket on the given port. */
static int create_socket(uint16_t port)
{
int fd;
struct sockaddr_in sa = {0};
/* Retrieve the file descriptor for a new UDP socket */
if ((fd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
fprintf(stderr, "cannot create socket");
goto err;
}
sa.sin_family = AF_INET;
sa.sin_port = htons(port);
/* Bind to the new UDP socket on localhost */
if (bind(fd, (const struct sockaddr *)&sa, sizeof(sa)) < 0) {
fprintf(stderr, "cannot bind to %u\n", port);
BIO_closesocket(fd);
goto err;
}
return fd;
err:
BIO_closesocket(fd);
return -1;
}
/*
* Main loop for server to accept QUIC connections.
* Echo every request back to the client.
*/
static int run_quic_server(SSL_CTX *ctx, int fd)
{
int ok = 0;
SSL *listener, *conn;
unsigned char buf[8192];
size_t nread;
size_t nwritten;
/*
* Create a new QUIC listener. Listeners, and other QUIC objects, default
* to operating in blocking mode. The configured behaviour is inherited by
* child objects.
*/
if ((listener = SSL_new_listener(ctx, 0)) == NULL)
goto err;
/* Provide the listener with our UDP socket. */
if (!SSL_set_fd(listener, fd))
goto err;
/* Begin listening. */
if (!SSL_listen(listener))
goto err;
/*
* Begin an infinite loop of listening for connections. We will only
* exit this loop if we encounter an error.
*/
for (;;) {
/* Pristine error stack for each new connection */
ERR_clear_error();
/* Block while waiting for a client connection */
printf("Waiting for connection\n");
conn = SSL_accept_connection(listener, 0);
if (conn == NULL) {
fprintf(stderr, "error while accepting connection\n");
goto err;
}
printf("Accepted new connection\n");
/* Echo client input */
while (SSL_read_ex(conn, buf, sizeof(buf), &nread) > 0) {
if (SSL_write_ex(conn, buf, nread, &nwritten) > 0
&& nwritten == nread)
break;
fprintf(stderr, "Error echoing client input");
break;
}
/* Signal the end of the stream. */
if (SSL_stream_conclude(conn, 0) != 1) {
fprintf(stderr, "Unable to conclude stream\n");
SSL_free(conn);
goto err;
}
/*
* Shut down the connection. We may need to call this multiple times
* to ensure the connection is shutdown completely.
*/
while (SSL_shutdown(conn) != 1)
continue;
SSL_free(conn);
}
err:
SSL_free(listener);
return ok;
}
/* Minimal QUIC HTTP/1.0 server. */
int main(int argc, char *argv[])
{
int res = EXIT_FAILURE;
SSL_CTX *ctx = NULL;
int fd;
unsigned long port;
#ifdef _WIN32
static const char *progname;
progname = argv[0];
#endif
if (argc != 4)
errx(res, "usage: %s <port> <server.crt> <server.key>", argv[0]);
/* Create SSL_CTX that supports QUIC. */
if ((ctx = create_ctx(argv[2], argv[3])) == NULL) {
ERR_print_errors_fp(stderr);
errx(res, "Failed to create context");
}
/* Parse port number from command line arguments. */
port = strtoul(argv[1], NULL, 0);
if (port == 0 || port > UINT16_MAX) {
SSL_CTX_free(ctx);
errx(res, "Failed to parse port number");
}
/* Create and bind a UDP socket. */
if ((fd = create_socket((uint16_t)port)) < 0) {
SSL_CTX_free(ctx);
ERR_print_errors_fp(stderr);
errx(res, "Failed to create socket");
}
/* QUIC server connection acceptance loop. */
if (!run_quic_server(ctx, fd)) {
SSL_CTX_free(ctx);
BIO_closesocket(fd);
ERR_print_errors_fp(stderr);
errx(res, "Error in QUIC server loop");
}
/* Free resources. */
SSL_CTX_free(ctx);
BIO_closesocket(fd);
res = EXIT_SUCCESS;
return res;
}

View file

@ -250,6 +250,29 @@ slightly differently. With QUIC the stream can be reset by the peer (which is
fatal for that stream), but the underlying connection itself may still be
healthy.
First, we write the entire request to the stream. We also must make sure to
signal to the server that we have finished writing. This can be done by passing
the SSL_WRITE_FLAG_CONCLUDE flag to L<SSL_write_ex2(3)> or by calling
L<SSL_stream_conclude(3)>. Since the first way is more efficient, we choose to
do that.
/* Write an HTTP GET request to the peer */
if (!SSL_write_ex(ssl, request_start, strlen(request_start), &written)) {
printf("Failed to write start of HTTP request\n");
goto end;
}
if (!SSL_write_ex(ssl, hostname, strlen(hostname), &written)) {
printf("Failed to write hostname in HTTP request\n");
goto end;
}
if (!SSL_write_ex2(ssl, request_end, strlen(request_end),
SSL_WRITE_FLAG_CONCLUDE, &written)) {
printf("Failed to write end of HTTP request\n");
goto end;
}
Then, we read the response from the server.
/*
* Get up to sizeof(buf) bytes of the response. We keep reading until the
* server closes the connection.

View file

@ -344,6 +344,12 @@ contains that data may change location. See L<SSL_CTX_set_mode(3)> for further
details. As in the TLS tutorials (L<ossl-guide-tls-client-block(7)>) we write
the request in three chunks.
First, we write the entire request to the stream. We also must make sure to
signal to the server that we have finished writing. This can be done by passing
the SSL_WRITE_FLAG_CONCLUDE flag to L<SSL_write_ex2(3)> or by calling
L<SSL_stream_conclude(3)>. Since the first way is more efficient, we choose to
do that.
/* Write an HTTP GET request to the peer */
while (!SSL_write_ex(ssl, request_start, strlen(request_start), &written)) {
if (handle_io_failure(ssl, 0) == 1)
@ -357,7 +363,8 @@ the request in three chunks.
printf("Failed to write hostname in HTTP request\n");
goto end; /* Cannot retry: error */
}
while (!SSL_write_ex(ssl, request_end, strlen(request_end), &written)) {
while (!SSL_write_ex2(ssl, request_end, strlen(request_end),
SSL_WRITE_FLAG_CONCLUDE, &written)) {
if (handle_io_failure(ssl, 0) == 1)
continue; /* Retry */
printf("Failed to write end of HTTP request\n");

View file

@ -5,10 +5,3 @@ ENDIF
SCRIPTS{noinst}=wrap.pl
SOURCE[wrap.pl]=wrap.pl.in
DEPEND[wrap.pl]=../configdata.pm
IF[{- !$disabled{quic} && !$disabled{stdio} && !$disabled{apps} -}]
PROGRAMS{noinst}=quicserver
SOURCE[quicserver]=quicserver.c
INCLUDE[quicserver]=../include ../apps/include
DEPEND[quicserver]=../libcrypto.a ../libssl.a
ENDIF

View file

@ -1,314 +0,0 @@
/*
* Copyright 2023 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
/*
* This is a temporary test server for QUIC. It will eventually be replaced
* by s_server and removed once we have full QUIC server support.
*/
#include <stdio.h>
#include <openssl/bio.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include "internal/e_os.h"
#include "internal/sockets.h"
#include "internal/quic_tserver.h"
#include "internal/quic_stream_map.h"
#include "internal/time.h"
static BIO *bio_err = NULL;
static void wait_for_activity(QUIC_TSERVER *qtserv)
{
fd_set readfds, writefds;
fd_set *readfdsp = NULL, *writefdsp = NULL;
struct timeval timeout, *timeoutp = NULL;
int width;
int sock;
BIO *bio = ossl_quic_tserver_get0_rbio(qtserv);
OSSL_TIME deadline;
BIO_get_fd(bio, &sock);
if (ossl_quic_tserver_get_net_read_desired(qtserv)) {
readfdsp = &readfds;
FD_ZERO(readfdsp);
openssl_fdset(sock, readfdsp);
}
if (ossl_quic_tserver_get_net_write_desired(qtserv)) {
writefdsp = &writefds;
FD_ZERO(writefdsp);
openssl_fdset(sock, writefdsp);
}
deadline = ossl_quic_tserver_get_deadline(qtserv);
if (!ossl_time_is_infinite(deadline)) {
timeout = ossl_time_to_timeval(ossl_time_subtract(deadline,
ossl_time_now()));
timeoutp = &timeout;
}
width = sock + 1;
if (readfdsp == NULL && writefdsp == NULL && timeoutp == NULL)
return;
select(width, readfdsp, writefdsp, NULL, timeoutp);
}
/* Helper function to create a BIO connected to the server */
static BIO *create_dgram_bio(int family, const char *hostname, const char *port)
{
int sock = -1;
BIO_ADDRINFO *res;
const BIO_ADDRINFO *ai = NULL;
BIO *bio;
if (BIO_sock_init() != 1)
return NULL;
/*
* Lookup IP address info for the server.
*/
if (!BIO_lookup_ex(hostname, port, BIO_LOOKUP_SERVER, family, SOCK_DGRAM,
0, &res))
return NULL;
/*
* Loop through all the possible addresses for the server and find one
* we can create and start listening on
*/
for (ai = res; ai != NULL; ai = BIO_ADDRINFO_next(ai)) {
/* Create the UDP socket */
sock = BIO_socket(BIO_ADDRINFO_family(ai), SOCK_DGRAM, 0, 0);
if (sock == -1)
continue;
/* Start listening on the socket */
if (!BIO_listen(sock, BIO_ADDRINFO_address(ai), 0)) {
BIO_closesocket(sock);
continue;
}
/* Set to non-blocking mode */
if (!BIO_socket_nbio(sock, 1)) {
BIO_closesocket(sock);
continue;
}
break; /* stop searching if we found an addr */
}
/* Free the address information resources we allocated earlier */
BIO_ADDRINFO_free(res);
/* If we didn't bind any sockets, fail */
if (ai == NULL)
return NULL;
/* Create a BIO to wrap the socket */
bio = BIO_new(BIO_s_datagram());
if (bio == NULL) {
BIO_closesocket(sock);
return NULL;
}
/*
* Associate the newly created BIO with the underlying socket. By
* passing BIO_CLOSE here the socket will be automatically closed when
* the BIO is freed. Alternatively you can use BIO_NOCLOSE, in which
* case you must close the socket explicitly when it is no longer
* needed.
*/
BIO_set_fd(bio, sock, BIO_CLOSE);
return bio;
}
static void usage(void)
{
BIO_printf(bio_err, "quicserver [-6][-trace] hostname port certfile keyfile\n");
}
int main(int argc, char *argv[])
{
QUIC_TSERVER_ARGS tserver_args = {0};
QUIC_TSERVER *qtserv = NULL;
int ipv6 = 0, trace = 0;
int argnext = 1;
BIO *bio = NULL;
char *hostname, *port, *certfile, *keyfile;
int ret = EXIT_FAILURE;
unsigned char reqbuf[1024];
size_t numbytes, reqbytes = 0;
const char reqterm[] = {
'\r', '\n', '\r', '\n'
};
const char *response[] = {
"HTTP/1.0 200 ok\r\nContent-type: text/html\r\n\r\n<!DOCTYPE html>\n<html>\n<body>Hello world</body>\n</html>\n",
"HTTP/1.0 200 ok\r\nContent-type: text/html\r\n\r\n<!DOCTYPE html>\n<html>\n<body>Hello again</body>\n</html>\n",
"HTTP/1.0 200 ok\r\nContent-type: text/html\r\n\r\n<!DOCTYPE html>\n<html>\n<body>Another response</body>\n</html>\n",
"HTTP/1.0 200 ok\r\nContent-type: text/html\r\n\r\n<!DOCTYPE html>\n<html>\n<body>A message</body>\n</html>\n",
};
unsigned char alpn[] = { 8, 'h', 't', 't', 'p', '/', '1', '.', '0' };
int first = 1;
uint64_t streamid;
size_t respnum = 0;
bio_err = BIO_new_fp(stderr, BIO_NOCLOSE | BIO_FP_TEXT);
if (argc == 0 || bio_err == NULL)
goto end2;
while (argnext < argc) {
if (argv[argnext][0] != '-')
break;
if (strcmp(argv[argnext], "-6") == 0) {
ipv6 = 1;
} else if(strcmp(argv[argnext], "-trace") == 0) {
trace = 1;
} else {
BIO_printf(bio_err, "Unrecognised argument %s\n", argv[argnext]);
usage();
goto end2;
}
argnext++;
}
if (argc - argnext != 4) {
usage();
goto end2;
}
hostname = argv[argnext++];
port = argv[argnext++];
certfile = argv[argnext++];
keyfile = argv[argnext++];
bio = create_dgram_bio(ipv6 ? AF_INET6 : AF_INET, hostname, port);
if (bio == NULL || !BIO_up_ref(bio)) {
BIO_printf(bio_err, "Unable to create server socket\n");
goto end2;
}
tserver_args.libctx = NULL;
tserver_args.net_rbio = bio;
tserver_args.net_wbio = bio;
tserver_args.alpn = alpn;
tserver_args.alpnlen = sizeof(alpn);
tserver_args.ctx = NULL;
qtserv = ossl_quic_tserver_new(&tserver_args, certfile, keyfile);
if (qtserv == NULL) {
BIO_printf(bio_err, "Failed to create the QUIC_TSERVER\n");
goto end;
}
BIO_printf(bio_err, "Starting quicserver\n");
BIO_printf(bio_err,
"Note that this utility will be removed in a future OpenSSL version.\n");
BIO_printf(bio_err,
"For test purposes only. Not for use in a production environment.\n");
/* Ownership of the BIO is passed to qtserv */
bio = NULL;
if (trace)
#ifndef OPENSSL_NO_SSL_TRACE
ossl_quic_tserver_set_msg_callback(qtserv, SSL_trace, bio_err);
#else
BIO_printf(bio_err,
"Warning: -trace specified but no SSL tracing support present\n");
#endif
/* Wait for handshake to complete */
ossl_quic_tserver_tick(qtserv);
while(!ossl_quic_tserver_is_handshake_confirmed(qtserv)) {
wait_for_activity(qtserv);
ossl_quic_tserver_tick(qtserv);
if (ossl_quic_tserver_is_terminated(qtserv)) {
BIO_printf(bio_err, "Failed waiting for handshake completion\n");
ret = EXIT_FAILURE;
goto end;
}
}
for (;; respnum++) {
if (respnum >= OSSL_NELEM(response))
goto end;
/* Wait for an incoming stream */
do {
streamid = ossl_quic_tserver_pop_incoming_stream(qtserv);
if (streamid == UINT64_MAX)
wait_for_activity(qtserv);
ossl_quic_tserver_tick(qtserv);
if (ossl_quic_tserver_is_terminated(qtserv)) {
/* Assume we finished everything the clients wants from us */
ret = EXIT_SUCCESS;
goto end;
}
} while(streamid == UINT64_MAX);
/* Read the request */
do {
if (first)
first = 0;
else
wait_for_activity(qtserv);
ossl_quic_tserver_tick(qtserv);
if (ossl_quic_tserver_is_terminated(qtserv)) {
BIO_printf(bio_err, "Failed reading request\n");
ret = EXIT_FAILURE;
goto end;
}
if (ossl_quic_tserver_read(qtserv, streamid, reqbuf + reqbytes,
sizeof(reqbuf) - reqbytes,
&numbytes)) {
if (numbytes > 0)
fwrite(reqbuf + reqbytes, 1, numbytes, stdout);
reqbytes += numbytes;
}
} while (reqbytes < sizeof(reqterm)
|| memcmp(reqbuf + reqbytes - sizeof(reqterm), reqterm,
sizeof(reqterm)) != 0);
if ((streamid & QUIC_STREAM_DIR_UNI) != 0) {
/*
* Incoming stream was uni-directional. Create a server initiated
* uni-directional stream for the response.
*/
if (!ossl_quic_tserver_stream_new(qtserv, 1, &streamid)) {
BIO_printf(bio_err, "Failed creating response stream\n");
goto end;
}
}
/* Send the response */
ossl_quic_tserver_tick(qtserv);
if (!ossl_quic_tserver_write(qtserv, streamid,
(unsigned char *)response[respnum],
strlen(response[respnum]), &numbytes))
goto end;
if (!ossl_quic_tserver_conclude(qtserv, streamid))
goto end;
}
end:
/* Free twice because we did an up-ref */
BIO_free(bio);
end2:
BIO_free(bio);
ossl_quic_tserver_free(qtserv);
BIO_free(bio_err);
return ret;
}