From dad45ea769dc51e45b3aee5a376a3ee306704ac7 Mon Sep 17 00:00:00 2001 From: Andrew Dinh Date: Mon, 16 Sep 2024 03:26:47 +0800 Subject: [PATCH] Adds a new demo blocking QUIC server for use with the existing demo QUIC clients MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove util/quicserver.c Reviewed-by: Saša Nedvědický Reviewed-by: Neil Horman (Merged from https://github.com/openssl/openssl/pull/25465) --- .gitignore | 1 - demos/guide/Makefile | 4 +- demos/guide/README.md | 17 +- demos/guide/build.info | 8 +- demos/guide/quic-client-block.c | 3 +- demos/guide/quic-client-non-block.c | 3 +- demos/guide/quic-server-block.c | 310 +++++++++++++++++ doc/man7/ossl-guide-quic-client-block.pod | 23 ++ doc/man7/ossl-guide-quic-client-non-block.pod | 9 +- util/build.info | 7 - util/quicserver.c | 314 ------------------ 11 files changed, 359 insertions(+), 340 deletions(-) create mode 100644 demos/guide/quic-server-block.c delete mode 100644 util/quicserver.c diff --git a/.gitignore b/.gitignore index 23dfd7c8ee..231e1765c2 100644 --- a/.gitignore +++ b/.gitignore @@ -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 diff --git a/demos/guide/Makefile b/demos/guide/Makefile index 943d2f3ee7..12e2efd567 100644 --- a/demos/guide/Makefile +++ b/demos/guide/Makefile @@ -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) diff --git a/demos/guide/README.md b/demos/guide/README.md index 2e1bf05d8e..835c124989 100644 --- a/demos/guide/README.md +++ b/demos/guide/README.md @@ -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 --------------------------------- diff --git a/demos/guide/build.info b/demos/guide/build.info index 21219a34b5..f5c62dcd67 100644 --- a/demos/guide/build.info +++ b/demos/guide/build.info @@ -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 diff --git a/demos/guide/quic-client-block.c b/demos/guide/quic-client-block.c index baf5292c47..beea291138 100644 --- a/demos/guide/quic-client-block.c +++ b/demos/guide/quic-client-block.c @@ -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; } diff --git a/demos/guide/quic-client-non-block.c b/demos/guide/quic-client-non-block.c index 3e3627c5ed..6058a4d4c1 100644 --- a/demos/guide/quic-client-non-block.c +++ b/demos/guide/quic-client-non-block.c @@ -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"); diff --git a/demos/guide/quic-server-block.c b/demos/guide/quic-server-block.c new file mode 100644 index 0000000000..f451ce7c6d --- /dev/null +++ b/demos/guide/quic-server-block.c @@ -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 + +/* Include the appropriate header file for SOCK_STREAM */ +#ifdef _WIN32 /* Windows */ +# include +# include +#else /* Linux/Unix */ +# include +# include +# include +# include +# include +#endif + +#include +#include +#include +#include + +#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 ", 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; +} diff --git a/doc/man7/ossl-guide-quic-client-block.pod b/doc/man7/ossl-guide-quic-client-block.pod index ab018e4a22..5a91f76d04 100644 --- a/doc/man7/ossl-guide-quic-client-block.pod +++ b/doc/man7/ossl-guide-quic-client-block.pod @@ -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 or by calling +L. 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. diff --git a/doc/man7/ossl-guide-quic-client-non-block.pod b/doc/man7/ossl-guide-quic-client-non-block.pod index f07e9cc669..d51719b06f 100644 --- a/doc/man7/ossl-guide-quic-client-non-block.pod +++ b/doc/man7/ossl-guide-quic-client-non-block.pod @@ -344,6 +344,12 @@ contains that data may change location. See L for further details. As in the TLS tutorials (L) 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 or by calling +L. 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"); diff --git a/util/build.info b/util/build.info index e4aab44b38..aad7c50fee 100644 --- a/util/build.info +++ b/util/build.info @@ -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 diff --git a/util/quicserver.c b/util/quicserver.c deleted file mode 100644 index d752340882..0000000000 --- a/util/quicserver.c +++ /dev/null @@ -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 -#include -#include -#include -#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\n\nHello world\n\n", - "HTTP/1.0 200 ok\r\nContent-type: text/html\r\n\r\n\n\nHello again\n\n", - "HTTP/1.0 200 ok\r\nContent-type: text/html\r\n\r\n\n\nAnother response\n\n", - "HTTP/1.0 200 ok\r\nContent-type: text/html\r\n\r\n\n\nA message\n\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; -}