1232 lines
35 KiB
C
1232 lines
35 KiB
C
|
/*
|
||
|
* Copyright 2022-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 <assert.h>
|
||
|
#include <openssl/configuration.h>
|
||
|
#include <openssl/bio.h>
|
||
|
#include "internal/e_os.h" /* For struct timeval */
|
||
|
#include "quictestlib.h"
|
||
|
#include "ssltestlib.h"
|
||
|
#include "../testutil.h"
|
||
|
#if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
|
||
|
# include "../threadstest.h"
|
||
|
#endif
|
||
|
#include "internal/quic_ssl.h"
|
||
|
#include "internal/quic_wire_pkt.h"
|
||
|
#include "internal/quic_record_tx.h"
|
||
|
#include "internal/quic_error.h"
|
||
|
#include "internal/packet.h"
|
||
|
#include "internal/tsan_assist.h"
|
||
|
|
||
|
#define GROWTH_ALLOWANCE 1024
|
||
|
|
||
|
struct noise_args_data_st {
|
||
|
BIO *cbio;
|
||
|
BIO *sbio;
|
||
|
BIO *tracebio;
|
||
|
int flags;
|
||
|
};
|
||
|
|
||
|
struct qtest_fault {
|
||
|
QUIC_TSERVER *qtserv;
|
||
|
|
||
|
/* Plain packet mutations */
|
||
|
/* Header for the plaintext packet */
|
||
|
QUIC_PKT_HDR pplainhdr;
|
||
|
/* iovec for the plaintext packet data buffer */
|
||
|
OSSL_QTX_IOVEC pplainio;
|
||
|
/* Allocated size of the plaintext packet data buffer */
|
||
|
size_t pplainbuf_alloc;
|
||
|
qtest_fault_on_packet_plain_cb pplaincb;
|
||
|
void *pplaincbarg;
|
||
|
|
||
|
/* Handshake message mutations */
|
||
|
/* Handshake message buffer */
|
||
|
unsigned char *handbuf;
|
||
|
/* Allocated size of the handshake message buffer */
|
||
|
size_t handbufalloc;
|
||
|
/* Actual length of the handshake message */
|
||
|
size_t handbuflen;
|
||
|
qtest_fault_on_handshake_cb handshakecb;
|
||
|
void *handshakecbarg;
|
||
|
qtest_fault_on_enc_ext_cb encextcb;
|
||
|
void *encextcbarg;
|
||
|
|
||
|
/* Cipher packet mutations */
|
||
|
qtest_fault_on_packet_cipher_cb pciphercb;
|
||
|
void *pciphercbarg;
|
||
|
|
||
|
/* Datagram mutations */
|
||
|
qtest_fault_on_datagram_cb datagramcb;
|
||
|
void *datagramcbarg;
|
||
|
/* The currently processed message */
|
||
|
BIO_MSG msg;
|
||
|
/* Allocated size of msg data buffer */
|
||
|
size_t msgalloc;
|
||
|
struct noise_args_data_st noiseargs;
|
||
|
};
|
||
|
|
||
|
static void packet_plain_finish(void *arg);
|
||
|
static void handshake_finish(void *arg);
|
||
|
static OSSL_TIME qtest_get_time(void);
|
||
|
static void qtest_reset_time(void);
|
||
|
|
||
|
static int using_fake_time = 0;
|
||
|
static OSSL_TIME fake_now;
|
||
|
static CRYPTO_RWLOCK *fake_now_lock = NULL;
|
||
|
|
||
|
static OSSL_TIME fake_now_cb(void *arg)
|
||
|
{
|
||
|
return qtest_get_time();
|
||
|
}
|
||
|
|
||
|
static void noise_msg_callback(int write_p, int version, int content_type,
|
||
|
const void *buf, size_t len, SSL *ssl,
|
||
|
void *arg)
|
||
|
{
|
||
|
struct noise_args_data_st *noiseargs = (struct noise_args_data_st *)arg;
|
||
|
|
||
|
if (content_type == SSL3_RT_QUIC_FRAME_FULL) {
|
||
|
PACKET pkt;
|
||
|
uint64_t frame_type;
|
||
|
|
||
|
if (!PACKET_buf_init(&pkt, buf, len))
|
||
|
return;
|
||
|
|
||
|
if (!ossl_quic_wire_peek_frame_header(&pkt, &frame_type, NULL))
|
||
|
return;
|
||
|
|
||
|
if (frame_type == OSSL_QUIC_FRAME_TYPE_PING) {
|
||
|
/*
|
||
|
* If either endpoint issues a ping frame then we are in danger
|
||
|
* of our noise being too much such that the connection itself
|
||
|
* fails. We back off on the noise for a bit to avoid that.
|
||
|
*/
|
||
|
(void)BIO_ctrl(noiseargs->cbio, BIO_CTRL_NOISE_BACK_OFF, 0, NULL);
|
||
|
(void)BIO_ctrl(noiseargs->sbio, BIO_CTRL_NOISE_BACK_OFF, 0, NULL);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#ifndef OPENSSL_NO_SSL_TRACE
|
||
|
if ((noiseargs->flags & QTEST_FLAG_CLIENT_TRACE) != 0
|
||
|
&& !SSL_is_server(ssl))
|
||
|
SSL_trace(write_p, version, content_type, buf, len, ssl,
|
||
|
noiseargs->tracebio);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
int qtest_create_quic_objects(OSSL_LIB_CTX *libctx, SSL_CTX *clientctx,
|
||
|
SSL_CTX *serverctx, char *certfile, char *keyfile,
|
||
|
int flags, QUIC_TSERVER **qtserv, SSL **cssl,
|
||
|
QTEST_FAULT **fault, BIO **tracebio)
|
||
|
{
|
||
|
/* ALPN value as recognised by QUIC_TSERVER */
|
||
|
unsigned char alpn[] = { 8, 'o', 's', 's', 'l', 't', 'e', 's', 't' };
|
||
|
QUIC_TSERVER_ARGS tserver_args = {0};
|
||
|
BIO *cbio = NULL, *sbio = NULL, *fisbio = NULL;
|
||
|
BIO_ADDR *peeraddr = NULL;
|
||
|
struct in_addr ina = {0};
|
||
|
BIO *tmpbio = NULL;
|
||
|
|
||
|
*qtserv = NULL;
|
||
|
if (*cssl == NULL) {
|
||
|
*cssl = SSL_new(clientctx);
|
||
|
if (!TEST_ptr(*cssl))
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
if (fault != NULL) {
|
||
|
*fault = OPENSSL_zalloc(sizeof(**fault));
|
||
|
if (*fault == NULL)
|
||
|
goto err;
|
||
|
}
|
||
|
|
||
|
#ifndef OPENSSL_NO_SSL_TRACE
|
||
|
if ((flags & QTEST_FLAG_CLIENT_TRACE) != 0) {
|
||
|
tmpbio = BIO_new_fp(stdout, BIO_NOCLOSE);
|
||
|
if (!TEST_ptr(tmpbio))
|
||
|
goto err;
|
||
|
|
||
|
SSL_set_msg_callback(*cssl, SSL_trace);
|
||
|
SSL_set_msg_callback_arg(*cssl, tmpbio);
|
||
|
}
|
||
|
#endif
|
||
|
if (tracebio != NULL)
|
||
|
*tracebio = tmpbio;
|
||
|
|
||
|
/* SSL_set_alpn_protos returns 0 for success! */
|
||
|
if (!TEST_false(SSL_set_alpn_protos(*cssl, alpn, sizeof(alpn))))
|
||
|
goto err;
|
||
|
|
||
|
if (!TEST_ptr(peeraddr = BIO_ADDR_new()))
|
||
|
goto err;
|
||
|
|
||
|
if ((flags & QTEST_FLAG_BLOCK) != 0) {
|
||
|
#if !defined(OPENSSL_NO_POSIX_IO)
|
||
|
int cfd, sfd;
|
||
|
|
||
|
/*
|
||
|
* For blocking mode we need to create actual sockets rather than doing
|
||
|
* everything in memory
|
||
|
*/
|
||
|
if (!TEST_true(create_test_sockets(&cfd, &sfd, SOCK_DGRAM, peeraddr)))
|
||
|
goto err;
|
||
|
cbio = BIO_new_dgram(cfd, 1);
|
||
|
if (!TEST_ptr(cbio)) {
|
||
|
close(cfd);
|
||
|
close(sfd);
|
||
|
goto err;
|
||
|
}
|
||
|
sbio = BIO_new_dgram(sfd, 1);
|
||
|
if (!TEST_ptr(sbio)) {
|
||
|
close(sfd);
|
||
|
goto err;
|
||
|
}
|
||
|
#else
|
||
|
goto err;
|
||
|
#endif
|
||
|
} else {
|
||
|
if (!TEST_true(BIO_new_bio_dgram_pair(&cbio, 0, &sbio, 0)))
|
||
|
goto err;
|
||
|
|
||
|
if (!TEST_true(BIO_dgram_set_caps(cbio, BIO_DGRAM_CAP_HANDLES_DST_ADDR))
|
||
|
|| !TEST_true(BIO_dgram_set_caps(sbio, BIO_DGRAM_CAP_HANDLES_DST_ADDR)))
|
||
|
goto err;
|
||
|
|
||
|
/* Dummy server address */
|
||
|
if (!TEST_true(BIO_ADDR_rawmake(peeraddr, AF_INET, &ina, sizeof(ina),
|
||
|
htons(0))))
|
||
|
goto err;
|
||
|
}
|
||
|
|
||
|
if ((flags & QTEST_FLAG_PACKET_SPLIT) != 0) {
|
||
|
BIO *pktsplitbio = BIO_new(bio_f_pkt_split_dgram_filter());
|
||
|
|
||
|
if (!TEST_ptr(pktsplitbio))
|
||
|
goto err;
|
||
|
cbio = BIO_push(pktsplitbio, cbio);
|
||
|
|
||
|
pktsplitbio = BIO_new(bio_f_pkt_split_dgram_filter());
|
||
|
if (!TEST_ptr(pktsplitbio))
|
||
|
goto err;
|
||
|
sbio = BIO_push(pktsplitbio, sbio);
|
||
|
}
|
||
|
|
||
|
if ((flags & QTEST_FLAG_NOISE) != 0) {
|
||
|
BIO *noisebio;
|
||
|
|
||
|
/*
|
||
|
* It is an error to not have a QTEST_FAULT object when introducing noise
|
||
|
*/
|
||
|
if (!TEST_ptr(fault))
|
||
|
goto err;
|
||
|
|
||
|
noisebio = BIO_new(bio_f_noisy_dgram_filter());
|
||
|
|
||
|
if (!TEST_ptr(noisebio))
|
||
|
goto err;
|
||
|
cbio = BIO_push(noisebio, cbio);
|
||
|
|
||
|
noisebio = BIO_new(bio_f_noisy_dgram_filter());
|
||
|
|
||
|
if (!TEST_ptr(noisebio))
|
||
|
goto err;
|
||
|
sbio = BIO_push(noisebio, sbio);
|
||
|
/*
|
||
|
* TODO(QUIC SERVER):
|
||
|
* Currently the simplistic handler of the quic tserver cannot cope
|
||
|
* with noise introduced in the first packet received from the
|
||
|
* client. This needs to be removed once we have proper server side
|
||
|
* handling.
|
||
|
*/
|
||
|
(void)BIO_ctrl(sbio, BIO_CTRL_NOISE_BACK_OFF, 0, NULL);
|
||
|
|
||
|
(*fault)->noiseargs.cbio = cbio;
|
||
|
(*fault)->noiseargs.sbio = sbio;
|
||
|
(*fault)->noiseargs.tracebio = tmpbio;
|
||
|
(*fault)->noiseargs.flags = flags;
|
||
|
|
||
|
SSL_set_msg_callback(*cssl, noise_msg_callback);
|
||
|
SSL_set_msg_callback_arg(*cssl, &(*fault)->noiseargs);
|
||
|
}
|
||
|
|
||
|
SSL_set_bio(*cssl, cbio, cbio);
|
||
|
|
||
|
if (!TEST_true(SSL_set_blocking_mode(*cssl,
|
||
|
(flags & QTEST_FLAG_BLOCK) != 0 ? 1 : 0)))
|
||
|
goto err;
|
||
|
|
||
|
if (!TEST_true(SSL_set1_initial_peer_addr(*cssl, peeraddr)))
|
||
|
goto err;
|
||
|
|
||
|
fisbio = BIO_new(qtest_get_bio_method());
|
||
|
if (!TEST_ptr(fisbio))
|
||
|
goto err;
|
||
|
|
||
|
BIO_set_data(fisbio, fault == NULL ? NULL : *fault);
|
||
|
|
||
|
if (!BIO_up_ref(sbio))
|
||
|
goto err;
|
||
|
if (!TEST_ptr(BIO_push(fisbio, sbio))) {
|
||
|
BIO_free(sbio);
|
||
|
goto err;
|
||
|
}
|
||
|
|
||
|
tserver_args.libctx = libctx;
|
||
|
tserver_args.net_rbio = sbio;
|
||
|
tserver_args.net_wbio = fisbio;
|
||
|
tserver_args.alpn = NULL;
|
||
|
if (serverctx != NULL && !TEST_true(SSL_CTX_up_ref(serverctx)))
|
||
|
goto err;
|
||
|
tserver_args.ctx = serverctx;
|
||
|
if (fake_now_lock == NULL) {
|
||
|
fake_now_lock = CRYPTO_THREAD_lock_new();
|
||
|
if (fake_now_lock == NULL)
|
||
|
goto err;
|
||
|
}
|
||
|
if ((flags & QTEST_FLAG_FAKE_TIME) != 0) {
|
||
|
using_fake_time = 1;
|
||
|
qtest_reset_time();
|
||
|
tserver_args.now_cb = fake_now_cb;
|
||
|
(void)ossl_quic_conn_set_override_now_cb(*cssl, fake_now_cb, NULL);
|
||
|
} else {
|
||
|
using_fake_time = 0;
|
||
|
}
|
||
|
|
||
|
if (!TEST_ptr(*qtserv = ossl_quic_tserver_new(&tserver_args, certfile,
|
||
|
keyfile)))
|
||
|
goto err;
|
||
|
|
||
|
/* Ownership of fisbio and sbio is now held by *qtserv */
|
||
|
sbio = NULL;
|
||
|
fisbio = NULL;
|
||
|
|
||
|
if ((flags & QTEST_FLAG_NOISE) != 0)
|
||
|
ossl_quic_tserver_set_msg_callback(*qtserv, noise_msg_callback,
|
||
|
&(*fault)->noiseargs);
|
||
|
|
||
|
if (fault != NULL)
|
||
|
(*fault)->qtserv = *qtserv;
|
||
|
|
||
|
BIO_ADDR_free(peeraddr);
|
||
|
|
||
|
return 1;
|
||
|
err:
|
||
|
SSL_CTX_free(tserver_args.ctx);
|
||
|
BIO_ADDR_free(peeraddr);
|
||
|
BIO_free_all(cbio);
|
||
|
BIO_free_all(fisbio);
|
||
|
BIO_free_all(sbio);
|
||
|
SSL_free(*cssl);
|
||
|
*cssl = NULL;
|
||
|
ossl_quic_tserver_free(*qtserv);
|
||
|
if (fault != NULL)
|
||
|
OPENSSL_free(*fault);
|
||
|
BIO_free(tmpbio);
|
||
|
if (tracebio != NULL)
|
||
|
*tracebio = NULL;
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void qtest_add_time(uint64_t millis)
|
||
|
{
|
||
|
if (!CRYPTO_THREAD_write_lock(fake_now_lock))
|
||
|
return;
|
||
|
fake_now = ossl_time_add(fake_now, ossl_ms2time(millis));
|
||
|
CRYPTO_THREAD_unlock(fake_now_lock);
|
||
|
}
|
||
|
|
||
|
static OSSL_TIME qtest_get_time(void)
|
||
|
{
|
||
|
OSSL_TIME ret;
|
||
|
|
||
|
if (!CRYPTO_THREAD_read_lock(fake_now_lock))
|
||
|
return ossl_time_zero();
|
||
|
ret = fake_now;
|
||
|
CRYPTO_THREAD_unlock(fake_now_lock);
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static void qtest_reset_time(void)
|
||
|
{
|
||
|
if (!CRYPTO_THREAD_write_lock(fake_now_lock))
|
||
|
return;
|
||
|
fake_now = ossl_time_zero();
|
||
|
CRYPTO_THREAD_unlock(fake_now_lock);
|
||
|
/* zero time can have a special meaning, bump it */
|
||
|
qtest_add_time(1);
|
||
|
}
|
||
|
|
||
|
QTEST_FAULT *qtest_create_injector(QUIC_TSERVER *ts)
|
||
|
{
|
||
|
QTEST_FAULT *f;
|
||
|
|
||
|
f = OPENSSL_zalloc(sizeof(*f));
|
||
|
if (f == NULL)
|
||
|
return NULL;
|
||
|
|
||
|
f->qtserv = ts;
|
||
|
return f;
|
||
|
|
||
|
}
|
||
|
|
||
|
int qtest_supports_blocking(void)
|
||
|
{
|
||
|
#if !defined(OPENSSL_NO_POSIX_IO) && defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
|
||
|
return 1;
|
||
|
#else
|
||
|
return 0;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
#define MAXLOOPS 1000
|
||
|
|
||
|
#if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
|
||
|
static int globserverret = 0;
|
||
|
static TSAN_QUALIFIER int abortserverthread = 0;
|
||
|
static QUIC_TSERVER *globtserv;
|
||
|
static const thread_t thread_zero;
|
||
|
|
||
|
static void run_server_thread(void)
|
||
|
{
|
||
|
/*
|
||
|
* This will operate in a busy loop because the server does not block,
|
||
|
* but should be acceptable because it is local and we expect this to be
|
||
|
* fast
|
||
|
*/
|
||
|
globserverret = qtest_create_quic_connection(globtserv, NULL);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
int qtest_wait_for_timeout(SSL *s, QUIC_TSERVER *qtserv)
|
||
|
{
|
||
|
struct timeval tv;
|
||
|
OSSL_TIME ctimeout, stimeout, mintimeout, now;
|
||
|
int cinf;
|
||
|
|
||
|
/* We don't need to wait in blocking mode */
|
||
|
if (s == NULL || SSL_get_blocking_mode(s))
|
||
|
return 1;
|
||
|
|
||
|
/* Don't wait if either BIO has data waiting */
|
||
|
if (BIO_pending(SSL_get_rbio(s)) > 0
|
||
|
|| BIO_pending(ossl_quic_tserver_get0_rbio(qtserv)) > 0)
|
||
|
return 1;
|
||
|
|
||
|
/*
|
||
|
* Neither endpoint has data waiting to be read. We assume data transmission
|
||
|
* is instantaneous due to using mem based BIOs, so there is no data "in
|
||
|
* flight" and no more data will be sent by either endpoint until some time
|
||
|
* based event has occurred. Therefore, wait for a timeout to occur. This
|
||
|
* might happen if we are using the noisy BIO and datagrams have been lost.
|
||
|
*/
|
||
|
if (!SSL_get_event_timeout(s, &tv, &cinf))
|
||
|
return 0;
|
||
|
|
||
|
if (using_fake_time)
|
||
|
now = qtest_get_time();
|
||
|
else
|
||
|
now = ossl_time_now();
|
||
|
|
||
|
ctimeout = cinf ? ossl_time_infinite() : ossl_time_from_timeval(tv);
|
||
|
stimeout = ossl_time_subtract(ossl_quic_tserver_get_deadline(qtserv), now);
|
||
|
mintimeout = ossl_time_min(ctimeout, stimeout);
|
||
|
if (ossl_time_is_infinite(mintimeout))
|
||
|
return 0;
|
||
|
|
||
|
if (using_fake_time)
|
||
|
qtest_add_time(ossl_time2ms(mintimeout));
|
||
|
else
|
||
|
OSSL_sleep(ossl_time2ms(mintimeout));
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
int qtest_create_quic_connection_ex(QUIC_TSERVER *qtserv, SSL *clientssl,
|
||
|
int wanterr)
|
||
|
{
|
||
|
int retc = -1, rets = 0, abortctr = 0, ret = 0;
|
||
|
int clienterr = 0, servererr = 0;
|
||
|
#if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
|
||
|
/*
|
||
|
* Pointless initialisation to avoid bogus compiler warnings about using
|
||
|
* t uninitialised
|
||
|
*/
|
||
|
thread_t t = thread_zero;
|
||
|
|
||
|
if (clientssl != NULL)
|
||
|
abortserverthread = 0;
|
||
|
#endif
|
||
|
|
||
|
if (!TEST_ptr(qtserv)) {
|
||
|
goto err;
|
||
|
} else if (clientssl == NULL) {
|
||
|
retc = 1;
|
||
|
} else if (SSL_get_blocking_mode(clientssl) > 0) {
|
||
|
#if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
|
||
|
/*
|
||
|
* clientssl is blocking. We will need a thread to complete the
|
||
|
* connection
|
||
|
*/
|
||
|
globtserv = qtserv;
|
||
|
if (!TEST_true(run_thread(&t, run_server_thread)))
|
||
|
goto err;
|
||
|
|
||
|
qtserv = NULL;
|
||
|
rets = 1;
|
||
|
#else
|
||
|
TEST_error("No thread support in this build");
|
||
|
goto err;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
do {
|
||
|
if (!clienterr && retc <= 0) {
|
||
|
int err;
|
||
|
|
||
|
retc = SSL_connect(clientssl);
|
||
|
if (retc <= 0) {
|
||
|
err = SSL_get_error(clientssl, retc);
|
||
|
|
||
|
if (err == wanterr) {
|
||
|
retc = 1;
|
||
|
#if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
|
||
|
if (qtserv == NULL && rets > 0)
|
||
|
tsan_store(&abortserverthread, 1);
|
||
|
else
|
||
|
#endif
|
||
|
rets = 1;
|
||
|
} else {
|
||
|
if (err != SSL_ERROR_WANT_READ
|
||
|
&& err != SSL_ERROR_WANT_WRITE) {
|
||
|
TEST_info("SSL_connect() failed %d, %d", retc, err);
|
||
|
TEST_openssl_errors();
|
||
|
clienterr = 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
qtest_add_time(1);
|
||
|
if (clientssl != NULL)
|
||
|
SSL_handle_events(clientssl);
|
||
|
if (qtserv != NULL)
|
||
|
ossl_quic_tserver_tick(qtserv);
|
||
|
|
||
|
if (!servererr && rets <= 0) {
|
||
|
servererr = ossl_quic_tserver_is_term_any(qtserv);
|
||
|
if (!servererr)
|
||
|
rets = ossl_quic_tserver_is_handshake_confirmed(qtserv);
|
||
|
}
|
||
|
|
||
|
if (clienterr && servererr)
|
||
|
goto err;
|
||
|
|
||
|
if (clientssl != NULL && ++abortctr == MAXLOOPS) {
|
||
|
TEST_info("No progress made");
|
||
|
goto err;
|
||
|
}
|
||
|
|
||
|
if ((retc <= 0 && !clienterr) || (rets <= 0 && !servererr)) {
|
||
|
if (!qtest_wait_for_timeout(clientssl, qtserv))
|
||
|
goto err;
|
||
|
}
|
||
|
} while ((retc <= 0 && !clienterr)
|
||
|
|| (rets <= 0 && !servererr
|
||
|
#if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
|
||
|
&& !tsan_load(&abortserverthread)
|
||
|
#endif
|
||
|
));
|
||
|
|
||
|
if (qtserv == NULL && rets > 0) {
|
||
|
#if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
|
||
|
if (!TEST_true(wait_for_thread(t)) || !TEST_true(globserverret))
|
||
|
goto err;
|
||
|
#else
|
||
|
TEST_error("Should not happen");
|
||
|
goto err;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
if (!clienterr && !servererr)
|
||
|
ret = 1;
|
||
|
err:
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int qtest_create_quic_connection(QUIC_TSERVER *qtserv, SSL *clientssl)
|
||
|
{
|
||
|
return qtest_create_quic_connection_ex(qtserv, clientssl, SSL_ERROR_NONE);
|
||
|
}
|
||
|
|
||
|
#if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
|
||
|
static TSAN_QUALIFIER int shutdowndone;
|
||
|
|
||
|
static void run_server_shutdown_thread(void)
|
||
|
{
|
||
|
/*
|
||
|
* This will operate in a busy loop because the server does not block,
|
||
|
* but should be acceptable because it is local and we expect this to be
|
||
|
* fast
|
||
|
*/
|
||
|
do {
|
||
|
ossl_quic_tserver_tick(globtserv);
|
||
|
} while(!tsan_load(&shutdowndone));
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
int qtest_shutdown(QUIC_TSERVER *qtserv, SSL *clientssl)
|
||
|
{
|
||
|
int tickserver = 1;
|
||
|
int ret = 0;
|
||
|
#if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
|
||
|
/*
|
||
|
* Pointless initialisation to avoid bogus compiler warnings about using
|
||
|
* t uninitialised
|
||
|
*/
|
||
|
thread_t t = thread_zero;
|
||
|
#endif
|
||
|
|
||
|
if (SSL_get_blocking_mode(clientssl) > 0) {
|
||
|
#if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
|
||
|
/*
|
||
|
* clientssl is blocking. We will need a thread to complete the
|
||
|
* connection
|
||
|
*/
|
||
|
globtserv = qtserv;
|
||
|
shutdowndone = 0;
|
||
|
if (!TEST_true(run_thread(&t, run_server_shutdown_thread)))
|
||
|
return 0;
|
||
|
|
||
|
tickserver = 0;
|
||
|
#else
|
||
|
TEST_error("No thread support in this build");
|
||
|
return 0;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
/* Busy loop in non-blocking mode. It should be quick because its local */
|
||
|
for (;;) {
|
||
|
int rc = SSL_shutdown(clientssl);
|
||
|
|
||
|
if (rc == 1) {
|
||
|
ret = 1;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (rc < 0)
|
||
|
break;
|
||
|
|
||
|
if (tickserver)
|
||
|
ossl_quic_tserver_tick(qtserv);
|
||
|
}
|
||
|
|
||
|
#if defined(OPENSSL_THREADS) && !defined(CRYPTO_TDEBUG)
|
||
|
tsan_store(&shutdowndone, 1);
|
||
|
if (!tickserver) {
|
||
|
if (!TEST_true(wait_for_thread(t)))
|
||
|
ret = 0;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
int qtest_check_server_transport_err(QUIC_TSERVER *qtserv, uint64_t code)
|
||
|
{
|
||
|
const QUIC_TERMINATE_CAUSE *cause;
|
||
|
|
||
|
ossl_quic_tserver_tick(qtserv);
|
||
|
|
||
|
/*
|
||
|
* Check that the server has closed with the specified code from the client
|
||
|
*/
|
||
|
if (!TEST_true(ossl_quic_tserver_is_term_any(qtserv)))
|
||
|
return 0;
|
||
|
|
||
|
cause = ossl_quic_tserver_get_terminate_cause(qtserv);
|
||
|
if (!TEST_ptr(cause)
|
||
|
|| !TEST_true(cause->remote)
|
||
|
|| !TEST_false(cause->app)
|
||
|
|| !TEST_uint64_t_eq(cause->error_code, code))
|
||
|
return 0;
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
int qtest_check_server_protocol_err(QUIC_TSERVER *qtserv)
|
||
|
{
|
||
|
return qtest_check_server_transport_err(qtserv, QUIC_ERR_PROTOCOL_VIOLATION);
|
||
|
}
|
||
|
|
||
|
int qtest_check_server_frame_encoding_err(QUIC_TSERVER *qtserv)
|
||
|
{
|
||
|
return qtest_check_server_transport_err(qtserv, QUIC_ERR_FRAME_ENCODING_ERROR);
|
||
|
}
|
||
|
|
||
|
void qtest_fault_free(QTEST_FAULT *fault)
|
||
|
{
|
||
|
if (fault == NULL)
|
||
|
return;
|
||
|
|
||
|
packet_plain_finish(fault);
|
||
|
handshake_finish(fault);
|
||
|
|
||
|
OPENSSL_free(fault);
|
||
|
}
|
||
|
|
||
|
static int packet_plain_mutate(const QUIC_PKT_HDR *hdrin,
|
||
|
const OSSL_QTX_IOVEC *iovecin, size_t numin,
|
||
|
QUIC_PKT_HDR **hdrout,
|
||
|
const OSSL_QTX_IOVEC **iovecout,
|
||
|
size_t *numout,
|
||
|
void *arg)
|
||
|
{
|
||
|
QTEST_FAULT *fault = arg;
|
||
|
size_t i, bufsz = 0;
|
||
|
unsigned char *cur;
|
||
|
|
||
|
/* Coalesce our data into a single buffer */
|
||
|
|
||
|
/* First calculate required buffer size */
|
||
|
for (i = 0; i < numin; i++)
|
||
|
bufsz += iovecin[i].buf_len;
|
||
|
|
||
|
fault->pplainio.buf_len = bufsz;
|
||
|
|
||
|
/* Add an allowance for possible growth */
|
||
|
bufsz += GROWTH_ALLOWANCE;
|
||
|
|
||
|
fault->pplainio.buf = cur = OPENSSL_malloc(bufsz);
|
||
|
if (cur == NULL) {
|
||
|
fault->pplainio.buf_len = 0;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
fault->pplainbuf_alloc = bufsz;
|
||
|
|
||
|
/* Copy in the data from the input buffers */
|
||
|
for (i = 0; i < numin; i++) {
|
||
|
memcpy(cur, iovecin[i].buf, iovecin[i].buf_len);
|
||
|
cur += iovecin[i].buf_len;
|
||
|
}
|
||
|
|
||
|
fault->pplainhdr = *hdrin;
|
||
|
|
||
|
/* Cast below is safe because we allocated the buffer */
|
||
|
if (fault->pplaincb != NULL
|
||
|
&& !fault->pplaincb(fault, &fault->pplainhdr,
|
||
|
(unsigned char *)fault->pplainio.buf,
|
||
|
fault->pplainio.buf_len, fault->pplaincbarg))
|
||
|
return 0;
|
||
|
|
||
|
*hdrout = &fault->pplainhdr;
|
||
|
*iovecout = &fault->pplainio;
|
||
|
*numout = 1;
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static void packet_plain_finish(void *arg)
|
||
|
{
|
||
|
QTEST_FAULT *fault = arg;
|
||
|
|
||
|
/* Cast below is safe because we allocated the buffer */
|
||
|
OPENSSL_free((unsigned char *)fault->pplainio.buf);
|
||
|
fault->pplainio.buf_len = 0;
|
||
|
fault->pplainbuf_alloc = 0;
|
||
|
fault->pplainio.buf = NULL;
|
||
|
}
|
||
|
|
||
|
int qtest_fault_set_packet_plain_listener(QTEST_FAULT *fault,
|
||
|
qtest_fault_on_packet_plain_cb pplaincb,
|
||
|
void *pplaincbarg)
|
||
|
{
|
||
|
fault->pplaincb = pplaincb;
|
||
|
fault->pplaincbarg = pplaincbarg;
|
||
|
|
||
|
return ossl_quic_tserver_set_plain_packet_mutator(fault->qtserv,
|
||
|
packet_plain_mutate,
|
||
|
packet_plain_finish,
|
||
|
fault);
|
||
|
}
|
||
|
|
||
|
/* To be called from a packet_plain_listener callback */
|
||
|
int qtest_fault_resize_plain_packet(QTEST_FAULT *fault, size_t newlen)
|
||
|
{
|
||
|
unsigned char *buf;
|
||
|
size_t oldlen = fault->pplainio.buf_len;
|
||
|
|
||
|
/*
|
||
|
* Alloc'd size should always be non-zero, so if this fails we've been
|
||
|
* incorrectly called
|
||
|
*/
|
||
|
if (fault->pplainbuf_alloc == 0)
|
||
|
return 0;
|
||
|
|
||
|
if (newlen > fault->pplainbuf_alloc) {
|
||
|
/* This exceeds our growth allowance. Fail */
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Cast below is safe because we allocated the buffer */
|
||
|
buf = (unsigned char *)fault->pplainio.buf;
|
||
|
|
||
|
if (newlen > oldlen) {
|
||
|
/* Extend packet with 0 bytes */
|
||
|
memset(buf + oldlen, 0, newlen - oldlen);
|
||
|
} /* else we're truncating or staying the same */
|
||
|
|
||
|
fault->pplainio.buf_len = newlen;
|
||
|
fault->pplainhdr.len = newlen;
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* Prepend frame data into a packet. To be called from a packet_plain_listener
|
||
|
* callback
|
||
|
*/
|
||
|
int qtest_fault_prepend_frame(QTEST_FAULT *fault, const unsigned char *frame,
|
||
|
size_t frame_len)
|
||
|
{
|
||
|
unsigned char *buf;
|
||
|
size_t old_len;
|
||
|
|
||
|
/*
|
||
|
* Alloc'd size should always be non-zero, so if this fails we've been
|
||
|
* incorrectly called
|
||
|
*/
|
||
|
if (fault->pplainbuf_alloc == 0)
|
||
|
return 0;
|
||
|
|
||
|
/* Cast below is safe because we allocated the buffer */
|
||
|
buf = (unsigned char *)fault->pplainio.buf;
|
||
|
old_len = fault->pplainio.buf_len;
|
||
|
|
||
|
/* Extend the size of the packet by the size of the new frame */
|
||
|
if (!TEST_true(qtest_fault_resize_plain_packet(fault,
|
||
|
old_len + frame_len)))
|
||
|
return 0;
|
||
|
|
||
|
memmove(buf + frame_len, buf, old_len);
|
||
|
memcpy(buf, frame, frame_len);
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static int handshake_mutate(const unsigned char *msgin, size_t msginlen,
|
||
|
unsigned char **msgout, size_t *msgoutlen,
|
||
|
void *arg)
|
||
|
{
|
||
|
QTEST_FAULT *fault = arg;
|
||
|
unsigned char *buf;
|
||
|
unsigned long payloadlen;
|
||
|
unsigned int msgtype;
|
||
|
PACKET pkt;
|
||
|
|
||
|
buf = OPENSSL_malloc(msginlen + GROWTH_ALLOWANCE);
|
||
|
if (buf == NULL)
|
||
|
return 0;
|
||
|
|
||
|
fault->handbuf = buf;
|
||
|
fault->handbuflen = msginlen;
|
||
|
fault->handbufalloc = msginlen + GROWTH_ALLOWANCE;
|
||
|
memcpy(buf, msgin, msginlen);
|
||
|
|
||
|
if (!PACKET_buf_init(&pkt, buf, msginlen)
|
||
|
|| !PACKET_get_1(&pkt, &msgtype)
|
||
|
|| !PACKET_get_net_3(&pkt, &payloadlen)
|
||
|
|| PACKET_remaining(&pkt) != payloadlen)
|
||
|
return 0;
|
||
|
|
||
|
/* Parse specific message types */
|
||
|
switch (msgtype) {
|
||
|
case SSL3_MT_ENCRYPTED_EXTENSIONS:
|
||
|
{
|
||
|
QTEST_ENCRYPTED_EXTENSIONS ee;
|
||
|
|
||
|
if (fault->encextcb == NULL)
|
||
|
break;
|
||
|
|
||
|
/*
|
||
|
* The EncryptedExtensions message is very simple. It just has an
|
||
|
* extensions block in it and nothing else.
|
||
|
*/
|
||
|
ee.extensions = (unsigned char *)PACKET_data(&pkt);
|
||
|
ee.extensionslen = payloadlen;
|
||
|
if (!fault->encextcb(fault, &ee, payloadlen, fault->encextcbarg))
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
/* No specific handlers for these message types yet */
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if (fault->handshakecb != NULL
|
||
|
&& !fault->handshakecb(fault, buf, fault->handbuflen,
|
||
|
fault->handshakecbarg))
|
||
|
return 0;
|
||
|
|
||
|
*msgout = buf;
|
||
|
*msgoutlen = fault->handbuflen;
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static void handshake_finish(void *arg)
|
||
|
{
|
||
|
QTEST_FAULT *fault = arg;
|
||
|
|
||
|
OPENSSL_free(fault->handbuf);
|
||
|
fault->handbuf = NULL;
|
||
|
}
|
||
|
|
||
|
int qtest_fault_set_handshake_listener(QTEST_FAULT *fault,
|
||
|
qtest_fault_on_handshake_cb handshakecb,
|
||
|
void *handshakecbarg)
|
||
|
{
|
||
|
fault->handshakecb = handshakecb;
|
||
|
fault->handshakecbarg = handshakecbarg;
|
||
|
|
||
|
return ossl_quic_tserver_set_handshake_mutator(fault->qtserv,
|
||
|
handshake_mutate,
|
||
|
handshake_finish,
|
||
|
fault);
|
||
|
}
|
||
|
|
||
|
int qtest_fault_set_hand_enc_ext_listener(QTEST_FAULT *fault,
|
||
|
qtest_fault_on_enc_ext_cb encextcb,
|
||
|
void *encextcbarg)
|
||
|
{
|
||
|
fault->encextcb = encextcb;
|
||
|
fault->encextcbarg = encextcbarg;
|
||
|
|
||
|
return ossl_quic_tserver_set_handshake_mutator(fault->qtserv,
|
||
|
handshake_mutate,
|
||
|
handshake_finish,
|
||
|
fault);
|
||
|
}
|
||
|
|
||
|
/* To be called from a handshake_listener callback */
|
||
|
int qtest_fault_resize_handshake(QTEST_FAULT *fault, size_t newlen)
|
||
|
{
|
||
|
unsigned char *buf;
|
||
|
size_t oldlen = fault->handbuflen;
|
||
|
|
||
|
/*
|
||
|
* Alloc'd size should always be non-zero, so if this fails we've been
|
||
|
* incorrectly called
|
||
|
*/
|
||
|
if (fault->handbufalloc == 0)
|
||
|
return 0;
|
||
|
|
||
|
if (newlen > fault->handbufalloc) {
|
||
|
/* This exceeds our growth allowance. Fail */
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
buf = (unsigned char *)fault->handbuf;
|
||
|
|
||
|
if (newlen > oldlen) {
|
||
|
/* Extend packet with 0 bytes */
|
||
|
memset(buf + oldlen, 0, newlen - oldlen);
|
||
|
} /* else we're truncating or staying the same */
|
||
|
|
||
|
fault->handbuflen = newlen;
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/* To be called from message specific listener callbacks */
|
||
|
int qtest_fault_resize_message(QTEST_FAULT *fault, size_t newlen)
|
||
|
{
|
||
|
/* First resize the underlying message */
|
||
|
if (!qtest_fault_resize_handshake(fault, newlen + SSL3_HM_HEADER_LENGTH))
|
||
|
return 0;
|
||
|
|
||
|
/* Fixup the handshake message header */
|
||
|
fault->handbuf[1] = (unsigned char)((newlen >> 16) & 0xff);
|
||
|
fault->handbuf[2] = (unsigned char)((newlen >> 8) & 0xff);
|
||
|
fault->handbuf[3] = (unsigned char)((newlen ) & 0xff);
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
int qtest_fault_delete_extension(QTEST_FAULT *fault,
|
||
|
unsigned int exttype, unsigned char *ext,
|
||
|
size_t *extlen,
|
||
|
BUF_MEM *old_ext)
|
||
|
{
|
||
|
PACKET pkt, sub, subext;
|
||
|
WPACKET old_ext_wpkt;
|
||
|
unsigned int type;
|
||
|
const unsigned char *start, *end;
|
||
|
size_t newlen, w;
|
||
|
size_t msglen = fault->handbuflen;
|
||
|
|
||
|
if (!PACKET_buf_init(&pkt, ext, *extlen))
|
||
|
return 0;
|
||
|
|
||
|
/* Extension block starts with 2 bytes for extension block length */
|
||
|
if (!PACKET_as_length_prefixed_2(&pkt, &sub))
|
||
|
return 0;
|
||
|
|
||
|
do {
|
||
|
start = PACKET_data(&sub);
|
||
|
if (!PACKET_get_net_2(&sub, &type)
|
||
|
|| !PACKET_get_length_prefixed_2(&sub, &subext))
|
||
|
return 0;
|
||
|
} while (type != exttype);
|
||
|
|
||
|
/* Found it */
|
||
|
end = PACKET_data(&sub);
|
||
|
|
||
|
if (old_ext != NULL) {
|
||
|
if (!WPACKET_init(&old_ext_wpkt, old_ext))
|
||
|
return 0;
|
||
|
|
||
|
if (!WPACKET_memcpy(&old_ext_wpkt, PACKET_data(&subext),
|
||
|
PACKET_remaining(&subext))
|
||
|
|| !WPACKET_get_total_written(&old_ext_wpkt, &w)) {
|
||
|
WPACKET_cleanup(&old_ext_wpkt);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
WPACKET_finish(&old_ext_wpkt);
|
||
|
old_ext->length = w;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* If we're not the last extension we need to move the rest earlier. The
|
||
|
* cast below is safe because we own the underlying buffer and we're no
|
||
|
* longer making PACKET calls.
|
||
|
*/
|
||
|
if (end < ext + *extlen)
|
||
|
memmove((unsigned char *)start, end, end - start);
|
||
|
|
||
|
/*
|
||
|
* Calculate new extensions payload length =
|
||
|
* Original length
|
||
|
* - 2 extension block length bytes
|
||
|
* - length of removed extension
|
||
|
*/
|
||
|
newlen = *extlen - 2 - (end - start);
|
||
|
|
||
|
/* Fixup the length bytes for the extension block */
|
||
|
ext[0] = (unsigned char)((newlen >> 8) & 0xff);
|
||
|
ext[1] = (unsigned char)((newlen ) & 0xff);
|
||
|
|
||
|
/*
|
||
|
* Length of the whole extension block is the new payload length plus the
|
||
|
* 2 bytes for the length
|
||
|
*/
|
||
|
*extlen = newlen + 2;
|
||
|
|
||
|
/* We can now resize the message */
|
||
|
if ((size_t)(end - start) + SSL3_HM_HEADER_LENGTH > msglen)
|
||
|
return 0; /* Should not happen */
|
||
|
msglen -= (end - start) + SSL3_HM_HEADER_LENGTH;
|
||
|
if (!qtest_fault_resize_message(fault, msglen))
|
||
|
return 0;
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
#define BIO_TYPE_CIPHER_PACKET_FILTER (0x80 | BIO_TYPE_FILTER)
|
||
|
|
||
|
static BIO_METHOD *pcipherbiometh = NULL;
|
||
|
|
||
|
# define BIO_MSG_N(array, stride, n) (*(BIO_MSG *)((char *)(array) + (n)*(stride)))
|
||
|
|
||
|
static int pcipher_sendmmsg(BIO *b, BIO_MSG *msg, size_t stride,
|
||
|
size_t num_msg, uint64_t flags,
|
||
|
size_t *num_processed)
|
||
|
{
|
||
|
QTEST_FAULT *fault;
|
||
|
BIO *next = BIO_next(b);
|
||
|
ossl_ssize_t ret = 0;
|
||
|
size_t i = 0, tmpnump;
|
||
|
QUIC_PKT_HDR hdr;
|
||
|
PACKET pkt;
|
||
|
unsigned char *tmpdata;
|
||
|
|
||
|
if (next == NULL)
|
||
|
return 0;
|
||
|
|
||
|
fault = BIO_get_data(b);
|
||
|
if (fault == NULL
|
||
|
|| (fault->pciphercb == NULL && fault->datagramcb == NULL))
|
||
|
return BIO_sendmmsg(next, msg, stride, num_msg, flags, num_processed);
|
||
|
|
||
|
if (num_msg == 0) {
|
||
|
*num_processed = 0;
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
for (i = 0; i < num_msg; ++i) {
|
||
|
fault->msg = BIO_MSG_N(msg, stride, i);
|
||
|
|
||
|
/* Take a copy of the data so that callbacks can modify it */
|
||
|
tmpdata = OPENSSL_malloc(fault->msg.data_len + GROWTH_ALLOWANCE);
|
||
|
if (tmpdata == NULL)
|
||
|
return 0;
|
||
|
memcpy(tmpdata, fault->msg.data, fault->msg.data_len);
|
||
|
fault->msg.data = tmpdata;
|
||
|
fault->msgalloc = fault->msg.data_len + GROWTH_ALLOWANCE;
|
||
|
|
||
|
if (fault->pciphercb != NULL) {
|
||
|
if (!PACKET_buf_init(&pkt, fault->msg.data, fault->msg.data_len))
|
||
|
return 0;
|
||
|
|
||
|
do {
|
||
|
if (!ossl_quic_wire_decode_pkt_hdr(&pkt,
|
||
|
/*
|
||
|
* TODO(QUIC SERVER):
|
||
|
* Needs to be set to the actual short header CID length
|
||
|
* when testing the server implementation.
|
||
|
*/
|
||
|
0,
|
||
|
1,
|
||
|
0, &hdr, NULL))
|
||
|
goto out;
|
||
|
|
||
|
/*
|
||
|
* hdr.data is const - but its our buffer so casting away the
|
||
|
* const is safe
|
||
|
*/
|
||
|
if (!fault->pciphercb(fault, &hdr, (unsigned char *)hdr.data,
|
||
|
hdr.len, fault->pciphercbarg))
|
||
|
goto out;
|
||
|
|
||
|
/*
|
||
|
* At the moment modifications to hdr by the callback
|
||
|
* are ignored. We might need to rewrite the QUIC header to
|
||
|
* enable tests to change this. We also don't yet have a
|
||
|
* mechanism for the callback to change the encrypted data
|
||
|
* length. It's not clear if that's needed or not.
|
||
|
*/
|
||
|
} while (PACKET_remaining(&pkt) > 0);
|
||
|
}
|
||
|
|
||
|
if (fault->datagramcb != NULL
|
||
|
&& !fault->datagramcb(fault, &fault->msg, stride,
|
||
|
fault->datagramcbarg))
|
||
|
goto out;
|
||
|
|
||
|
if (!BIO_sendmmsg(next, &fault->msg, stride, 1, flags, &tmpnump)) {
|
||
|
*num_processed = i;
|
||
|
goto out;
|
||
|
}
|
||
|
|
||
|
OPENSSL_free(fault->msg.data);
|
||
|
fault->msg.data = NULL;
|
||
|
fault->msgalloc = 0;
|
||
|
}
|
||
|
|
||
|
*num_processed = i;
|
||
|
out:
|
||
|
ret = i > 0;
|
||
|
OPENSSL_free(fault->msg.data);
|
||
|
fault->msg.data = NULL;
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
static long pcipher_ctrl(BIO *b, int cmd, long larg, void *parg)
|
||
|
{
|
||
|
BIO *next = BIO_next(b);
|
||
|
|
||
|
if (next == NULL)
|
||
|
return -1;
|
||
|
|
||
|
return BIO_ctrl(next, cmd, larg, parg);
|
||
|
}
|
||
|
|
||
|
BIO_METHOD *qtest_get_bio_method(void)
|
||
|
{
|
||
|
BIO_METHOD *tmp;
|
||
|
|
||
|
if (pcipherbiometh != NULL)
|
||
|
return pcipherbiometh;
|
||
|
|
||
|
tmp = BIO_meth_new(BIO_TYPE_CIPHER_PACKET_FILTER, "Cipher Packet Filter");
|
||
|
|
||
|
if (!TEST_ptr(tmp))
|
||
|
return NULL;
|
||
|
|
||
|
if (!TEST_true(BIO_meth_set_sendmmsg(tmp, pcipher_sendmmsg))
|
||
|
|| !TEST_true(BIO_meth_set_ctrl(tmp, pcipher_ctrl)))
|
||
|
goto err;
|
||
|
|
||
|
pcipherbiometh = tmp;
|
||
|
tmp = NULL;
|
||
|
err:
|
||
|
BIO_meth_free(tmp);
|
||
|
return pcipherbiometh;
|
||
|
}
|
||
|
|
||
|
int qtest_fault_set_packet_cipher_listener(QTEST_FAULT *fault,
|
||
|
qtest_fault_on_packet_cipher_cb pciphercb,
|
||
|
void *pciphercbarg)
|
||
|
{
|
||
|
fault->pciphercb = pciphercb;
|
||
|
fault->pciphercbarg = pciphercbarg;
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
int qtest_fault_set_datagram_listener(QTEST_FAULT *fault,
|
||
|
qtest_fault_on_datagram_cb datagramcb,
|
||
|
void *datagramcbarg)
|
||
|
{
|
||
|
fault->datagramcb = datagramcb;
|
||
|
fault->datagramcbarg = datagramcbarg;
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
/* To be called from a datagram_listener callback */
|
||
|
int qtest_fault_resize_datagram(QTEST_FAULT *fault, size_t newlen)
|
||
|
{
|
||
|
if (newlen > fault->msgalloc)
|
||
|
return 0;
|
||
|
|
||
|
if (newlen > fault->msg.data_len)
|
||
|
memset((unsigned char *)fault->msg.data + fault->msg.data_len, 0,
|
||
|
newlen - fault->msg.data_len);
|
||
|
|
||
|
fault->msg.data_len = newlen;
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
int bio_msg_copy(BIO_MSG *dst, BIO_MSG *src)
|
||
|
{
|
||
|
/*
|
||
|
* Note it is assumed that the originally allocated data sizes for dst and
|
||
|
* src are the same
|
||
|
*/
|
||
|
memcpy(dst->data, src->data, src->data_len);
|
||
|
dst->data_len = src->data_len;
|
||
|
dst->flags = src->flags;
|
||
|
if (dst->local != NULL) {
|
||
|
if (src->local != NULL) {
|
||
|
if (!TEST_true(BIO_ADDR_copy(dst->local, src->local)))
|
||
|
return 0;
|
||
|
} else {
|
||
|
BIO_ADDR_clear(dst->local);
|
||
|
}
|
||
|
}
|
||
|
if (!TEST_true(BIO_ADDR_copy(dst->peer, src->peer)))
|
||
|
return 0;
|
||
|
|
||
|
return 1;
|
||
|
}
|