/* * 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 */ #include #include #include #include #include "internal/common.h" #include "internal/sockets.h" #include "internal/time.h" #include "testutil.h" static const char msg1[] = "GET LICENSE.txt\r\n"; static char msg2[16000]; static int is_want(SSL *s, int ret) { int ec = SSL_get_error(s, ret); return ec == SSL_ERROR_WANT_READ || ec == SSL_ERROR_WANT_WRITE; } static int test_quic_client(void) { int testresult = 0, ret; int c_fd = INVALID_SOCKET; BIO *c_net_bio = NULL, *c_net_bio_own = NULL; BIO_ADDR *s_addr_ = NULL; struct in_addr ina = {0}; SSL_CTX *c_ctx = NULL; SSL *c_ssl = NULL; short port = 4433; int c_connected = 0, c_write_done = 0, c_shutdown = 0; size_t l = 0, c_total_read = 0; OSSL_TIME start_time; unsigned char alpn[] = { 8, 'h', 't', 't', 'p', '/', '0', '.', '9' }; ina.s_addr = htonl(0x7f000001UL); /* Setup test client. */ c_fd = BIO_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, 0); if (!TEST_int_ne(c_fd, INVALID_SOCKET)) goto err; if (!TEST_true(BIO_socket_nbio(c_fd, 1))) goto err; if (!TEST_ptr(s_addr_ = BIO_ADDR_new())) goto err; if (!TEST_true(BIO_ADDR_rawmake(s_addr_, AF_INET, &ina, sizeof(ina), htons(port)))) goto err; if (!TEST_ptr(c_net_bio = c_net_bio_own = BIO_new_dgram(c_fd, 0))) goto err; if (!BIO_dgram_set_peer(c_net_bio, s_addr_)) goto err; if (!TEST_ptr(c_ctx = SSL_CTX_new(OSSL_QUIC_client_method()))) goto err; if (!TEST_ptr(c_ssl = SSL_new(c_ctx))) goto err; /* 0 is a success for SSL_set_alpn_protos() */ if (!TEST_false(SSL_set_alpn_protos(c_ssl, alpn, sizeof(alpn)))) goto err; /* Takes ownership of our reference to the BIO. */ SSL_set0_rbio(c_ssl, c_net_bio); /* Get another reference to be transferred in the SSL_set0_wbio call. */ if (!TEST_true(BIO_up_ref(c_net_bio))) { c_net_bio_own = NULL; /* SSL_free will free the first reference. */ goto err; } SSL_set0_wbio(c_ssl, c_net_bio); c_net_bio_own = NULL; if (!TEST_true(SSL_set_blocking_mode(c_ssl, 0))) goto err; start_time = ossl_time_now(); for (;;) { if (ossl_time_compare(ossl_time_subtract(ossl_time_now(), start_time), ossl_ms2time(3000)) >= 0) { TEST_error("timeout while attempting QUIC client test"); goto err; } if (!c_connected) { ret = SSL_connect(c_ssl); if (!TEST_true(ret == 1 || is_want(c_ssl, ret))) goto err; if (ret == 1) { c_connected = 1; TEST_info("Connected!"); } } if (c_connected && !c_write_done) { if (!TEST_int_eq(SSL_write(c_ssl, msg1, sizeof(msg1) - 1), (int)sizeof(msg1) - 1)) goto err; if (!TEST_true(SSL_stream_conclude(c_ssl, 0))) goto err; c_write_done = 1; } if (c_write_done && !c_shutdown && c_total_read < sizeof(msg2) - 1) { ret = SSL_read_ex(c_ssl, msg2 + c_total_read, sizeof(msg2) - 1 - c_total_read, &l); if (ret != 1) { if (SSL_get_error(c_ssl, ret) == SSL_ERROR_ZERO_RETURN) { c_shutdown = 1; TEST_info("Message: \n%s\n", msg2); } else if (!TEST_true(is_want(c_ssl, ret))) { goto err; } } else { c_total_read += l; if (!TEST_size_t_lt(c_total_read, sizeof(msg2) - 1)) goto err; } } if (c_shutdown) { ret = SSL_shutdown(c_ssl); if (ret == 1) break; } /* * This is inefficient because we spin until things work without * blocking but this is just a test. */ OSSL_sleep(0); SSL_handle_events(c_ssl); } testresult = 1; err: SSL_free(c_ssl); SSL_CTX_free(c_ctx); BIO_ADDR_free(s_addr_); BIO_free(c_net_bio_own); if (c_fd != INVALID_SOCKET) BIO_closesocket(c_fd); return testresult; } OPT_TEST_DECLARE_USAGE("certfile privkeyfile\n") int setup_tests(void) { if (!test_skip_common_options()) { TEST_error("Error parsing test options\n"); return 0; } ADD_TEST(test_quic_client); return 1; }