Modify the QUIC HQ interop server/client to support both IPv4 and IPv6.
Reviewed-by: Neil Horman <nhorman@openssl.org>
Reviewed-by: Tomas Mraz <tomas@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/26194)
run_quic_interop:
strategy:
matrix:
- tests: [http3, transfer, handshake, retry, chacha20, resumption, amplificationlimit]
+ tests: [http3, transfer, handshake, retry, chacha20, resumption, amplificationlimit, ipv6]
servers: [quic-go, ngtcp2, mvfst, quiche, nginx, msquic, haproxy]
fail-fast: false
runs-on: ubuntu-latest
* This file implements a lightweight QUIC server supporting the HTTP/0.9
* protocol for interoperability testing. It includes functions for setting
* up a secure QUIC connection, handling ALPN negotiation, and serving client
- * requests. Intended for use with the quic-interop-runner
+ * requests. Intended for use with the quic-interop-runner
* available at https://interop.seemann.io
*
* Key functionalities:
#ifdef _WIN32
# include <stdarg.h>
# include <winsock2.h>
+# include <ws2tcpip.h>
#else
# include <sys/socket.h>
# include <netinet/in.h>
* connections.
*
* Notes:
- * - This function assumes the use of IPv4 (`AF_INET`) and UDP (`SOCK_DGRAM`).
+ * - This function assumes UDP (`SOCK_DGRAM`).
+ * - This function accepts on both IPv4 and IPv6.
* - The specified port is converted to network byte order using `htons`.
*/
static BIO *create_socket(uint16_t port)
int fd = -1;
BIO *sock = NULL;
BIO_ADDR *addr = NULL;
- struct in_addr ina;
+ int opt = 0;
+#ifdef _WIN32
+ struct in6_addr in6addr_any;
- ina.s_addr = INADDR_ANY;
+ memset(&in6addr_any, 0, sizeof(in6addr_any));
+#endif
/* Retrieve the file descriptor for a new UDP socket */
- if ((fd = BIO_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, 0)) < 0) {
+ if ((fd = BIO_socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP, 0)) < 0) {
fprintf(stderr, "cannot create socket");
goto err;
}
+ /*
+ * IPv6_V6ONLY is only available on some platforms. If it is defined,
+ * disable it to accept both IPv4 and IPv6 connections. Otherwise, the
+ * server will only accept IPv6 connections.
+ */
+#ifdef IPV6_V6ONLY
+ if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &opt, sizeof(opt)) < 0) {
+ fprintf(stderr, "setsockopt IPV6_V6ONLY failed");
+ goto err;
+ }
+#endif
+
/*
* Create a new BIO_ADDR
*/
}
/*
- * Build a INADDR_ANY BIO_ADDR
+ * Build an INADDR_ANY BIO_ADDR
*/
- if (!BIO_ADDR_rawmake(addr, AF_INET, &ina, sizeof(ina), htons(port))) {
+ if (!BIO_ADDR_rawmake(addr, AF_INET6, &in6addr_any, sizeof(in6addr_any), htons(port))) {
fprintf(stderr, "unable to bind to port %d\n", port);
goto err;
}
*
* @param hostname The hostname of the server to connect to.
* @param port The port number of the server to connect to.
- * @param family The desired address family (e.g., AF_INET for IPv4,
- * AF_INET6 for IPv6).
* @param peer_addr A pointer to a BIO_ADDR pointer that will hold the address
* of the connected peer on success. The caller is responsible
* for freeing this memory using BIO_ADDR_free().
* freed.
*/
static BIO *create_socket_bio(const char *hostname, const char *port,
- int family, BIO_ADDR **peer_addr)
+ BIO_ADDR **peer_addr)
{
int sock = -1;
BIO_ADDRINFO *res;
/*
* Lookup IP address info for the server.
*/
- if (!BIO_lookup_ex(hostname, port, BIO_LOOKUP_CLIENT, family, SOCK_DGRAM, 0,
- &res))
+ if (!BIO_lookup_ex(hostname, port, BIO_LOOKUP_CLIENT, AF_UNSPEC, SOCK_DGRAM,
+ 0, &res))
return NULL;
/*
*
* @param hostname Hostname to connect to.
* @param port Port to connect to.
- * @param ipv6 Whether to use IPv6 (non-zero for IPv6, zero for IPv4).
* @param ctx Pointer to an SSL_CTX object, which will be created.
* @param ssl Pointer to an SSL object, which will be created.
*
* @return Returns 0 on success, 1 on error.
*/
-static int setup_connection(char *hostname, char *port, int ipv6,
+static int setup_connection(char *hostname, char *port,
SSL_CTX **ctx, SSL **ssl)
{
unsigned char alpn[] = {10, 'h', 'q', '-', 'i', 'n', 't', 'e', 'r', 'o', 'p'};
* Create the underlying transport socket/BIO and associate it with the
* connection.
*/
- bio = create_socket_bio(hostname, port, ipv6 ? AF_INET6 : AF_INET,
- &peer_addr);
+ bio = create_socket_bio(hostname, port, &peer_addr);
if (bio == NULL) {
fprintf(stderr, "Failed to crete the BIO\n");
goto end;
*
* This function sets up an SSL/TLS connection using QUIC, sends HTTP GET
* requests for files specified in the command-line arguments, and saves
- * the responses to disk. It handles various configurations such as IPv6
- * support, session caching, and key logging.
+ * the responses to disk. It handles various configurations such as session
+ * caching, and key logging.
*
* @param argc The number of command-line arguments.
* @param argv The array of command-line arguments. The expected format is
- * "[-6] hostname port file".
+ * "hostname port file".
* @return EXIT_SUCCESS on success, or EXIT_FAILURE on error.
*
* @note The function performs the following main tasks:
- * - Parses command-line arguments and configures IPv6 if specified.
+ * - Parses command-line arguments.
* - Reads the list of requests from the specified file.
* - Sets up the SSL context and configures certificate verification.
* - Optionally enables key logging and session caching.
size_t this_poll_count = 0;
char *req = NULL;
char *hostname, *port;
- int ipv6 = 0;
if (argc < 4) {
- fprintf(stderr, "Usage: quic-hq-interop [-6] hostname port reqfile\n");
+ fprintf(stderr, "Usage: quic-hq-interop hostname port reqfile\n");
goto end;
}
- if (!strcmp(argv[argnext], "-6")) {
- if (argc < 5) {
- fprintf(stderr, "Usage: quic-hq-interop [-6] hostname port reqfile\n");
- goto end;
- }
- ipv6 = 1;
- argnext++;
- }
hostname = argv[argnext++];
port = argv[argnext++];
reqfile = argv[argnext];
req_bio = NULL;
reqnames[read_offset + 1] = '\0';
- if (!setup_connection(hostname, port, ipv6, &ctx, &ssl)) {
+ if (!setup_connection(hostname, port, &ctx, &ssl)) {
fprintf(stderr, "Unable to establish connection\n");
goto end;
}
SSL_CERT_FILE=/certs/ca.pem curl --config $CURLRC || exit 1
exit 0
;;
- "handshake"|"transfer"|"retry")
- HOSTNAME=none
- for req in $REQUESTS
- do
- OUTFILE=$(basename $req)
- if [ "$HOSTNAME" == "none" ]
- then
- HOSTNAME=$(printf "%s\n" "$req" | sed -ne 's,^https://\([^/:]*\).*,\1,p')
- HOSTPORT=$(printf "%s\n" "$req" | sed -ne 's,^https://[^:/]*:\([^/]*\).*,\1,p')
- fi
- echo -n "$OUTFILE " >> ./reqfile.txt
- done
- SSLKEYLOGFILE=/logs/keys.log SSL_CERT_FILE=/certs/ca.pem SSL_CERT_DIR=/certs quic-hq-interop $HOSTNAME $HOSTPORT ./reqfile.txt || exit 1
- exit 0
- ;;
+ "handshake"|"transfer"|"retry"|"ipv6")
+ HOSTNAME=none
+ for req in $REQUESTS
+ do
+ OUTFILE=$(basename $req)
+ if [ "$HOSTNAME" == "none" ]
+ then
+ HOSTNAME=$(printf "%s\n" "$req" | sed -ne 's,^https://\([^/:]*\).*,\1,p')
+ HOSTPORT=$(printf "%s\n" "$req" | sed -ne 's,^https://[^:/]*:\([^/]*\).*,\1,p')
+ fi
+ echo -n "$OUTFILE " >> ./reqfile.txt
+ done
+ SSLKEYLOGFILE=/logs/keys.log SSL_CERT_FILE=/certs/ca.pem SSL_CERT_DIR=/certs quic-hq-interop $HOSTNAME $HOSTPORT ./reqfile.txt || exit 1
+ exit 0
+ ;;
"resumption")
- for req in $REQUESTS
- do
- OUTFILE=$(basename $req)
- echo -n "$OUTFILE " > ./reqfile.txt
- HOSTNAME=$(printf "%s\n" "$req" | sed -ne 's,^https://\([^/:]*\).*,\1,p')
- HOSTPORT=$(printf "%s\n" "$req" | sed -ne 's,^https://[^:/]*:\([^/]*\).*,\1,p')
- SSL_SESSION_FILE=./session.db SSLKEYLOGFILE=/logs/keys.log SSL_CERT_FILE=/certs/ca.pem SSL_CERT_DIR=/certs quic-hq-interop $HOSTNAME $HOSTPORT ./reqfile.txt || exit 1
- done
- exit 0
- ;;
+ for req in $REQUESTS
+ do
+ OUTFILE=$(basename $req)
+ echo -n "$OUTFILE " > ./reqfile.txt
+ HOSTNAME=$(printf "%s\n" "$req" | sed -ne 's,^https://\([^/:]*\).*,\1,p')
+ HOSTPORT=$(printf "%s\n" "$req" | sed -ne 's,^https://[^:/]*:\([^/]*\).*,\1,p')
+ SSL_SESSION_FILE=./session.db SSLKEYLOGFILE=/logs/keys.log SSL_CERT_FILE=/certs/ca.pem SSL_CERT_DIR=/certs quic-hq-interop $HOSTNAME $HOSTPORT ./reqfile.txt || exit 1
+ done
+ exit 0
+ ;;
"chacha20")
- for req in $REQUESTS
- do
- OUTFILE=$(basename $req)
- printf "%s " "$OUTFILE" >> ./reqfile.txt
- HOSTNAME=$(printf "%s\n" "$req" | sed -ne 's,^https://\([^/:]*\).*,\1,p')
- HOSTPORT=$(printf "%s\n" "$req" | sed -ne 's,^https://[^:/]*:\([^/]*\).*,\1,p')
- done
- SSL_CIPHER_SUITES=TLS_CHACHA20_POLY1305_SHA256 SSL_SESSION_FILE=./session.db SSLKEYLOGFILE=/logs/keys.log SSL_CERT_FILE=/certs/ca.pem SSL_CERT_DIR=/certs quic-hq-interop $HOSTNAME $HOSTPORT ./reqfile.txt || exit 1
- exit 0
- ;;
+ for req in $REQUESTS
+ do
+ OUTFILE=$(basename $req)
+ printf "%s " "$OUTFILE" >> ./reqfile.txt
+ HOSTNAME=$(printf "%s\n" "$req" | sed -ne 's,^https://\([^/:]*\).*,\1,p')
+ HOSTPORT=$(printf "%s\n" "$req" | sed -ne 's,^https://[^:/]*:\([^/]*\).*,\1,p')
+ done
+ SSL_CIPHER_SUITES=TLS_CHACHA20_POLY1305_SHA256 SSL_SESSION_FILE=./session.db SSLKEYLOGFILE=/logs/keys.log SSL_CERT_FILE=/certs/ca.pem SSL_CERT_DIR=/certs quic-hq-interop $HOSTNAME $HOSTPORT ./reqfile.txt || exit 1
+ exit 0
+ ;;
*)
echo "UNSUPPORTED TESTCASE $TESTCASE"
exit 127
echo "TESTCASE is $TESTCASE"
rm -f $CURLRC
case "$TESTCASE" in
- "handshake"|"transfer")
+ "handshake"|"transfer"|"ipv6")
NO_ADDR_VALIDATE=yes SSLKEYLOGFILE=/logs/keys.log FILEPREFIX=/www quic-hq-interop-server 443 /certs/cert.pem /certs/priv.key
;;
"retry"|"resumption")