return 1;
}
- ctx.qc->init_peer_addr = *peer_addr;
- return 1;
+ return BIO_ADDR_copy(&ctx.qc->init_peer_addr, peer_addr);
}
/*
/*
* SSL_new_from_listener
* ---------------------
+ * code here is derived from ossl_quic_new(). The `ssl` argument is
+ * a listener object which already comes with QUIC port/engine. The newly
+ * created QUIC connection object (QCSO) is going to share the port/engine
+ * with listener (`ssl`). The `ssl` also becomes a parent of QCSO created
+ * by this function. The caller uses QCSO instance to connect to
+ * remote QUIC server.
+ *
+ * The QCSO created here requires us to also create a channel so we
+ * can connect to remote server.
*/
SSL *ossl_quic_new_from_listener(SSL *ssl, uint64_t flags)
{
- /* TODO(QUIC SERVER): Implement SSL_new_from_listener */
+ QCTX ctx;
+ QUIC_CONNECTION *qc;
+ QUIC_LISTENER *ql;
+ SSL_CONNECTION *sc = NULL;
+
+ if (flags != 0)
+ return NULL;
+
+ if (!expect_quic_listener(ssl, &ctx))
+ return NULL;
+
+ if (!SSL_up_ref(&ctx.ql->obj.ssl))
+ return NULL;
+
+ qctx_lock(&ctx);
+
+ ql = ctx.ql;
+
+ if ((qc = OPENSSL_zalloc(sizeof(*qc))) == NULL) {
+ QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_CRYPTO_LIB, NULL);
+ goto err;
+ }
+
+ /*
+ * NOTE: setting a listener here is needed so `qc_cleanup()` does the right
+ * thing. Setting listener to ql avoids premature destruction of port in
+ * qc_cleanup()
+ */
+ qc->listener = ql;
+ qc->engine = ql->engine;
+ qc->port = ql->port;
+/* create channel */
+#if defined(OPENSSL_THREADS)
+ /* this is the engine mutex */
+ qc->mutex = ql->mutex;
+#endif
+#if !defined(OPENSSL_NO_QUIC_THREAD_ASSIST)
+ qc->is_thread_assisted
+ = ((ql->obj.domain_flags & SSL_DOMAIN_FLAG_THREAD_ASSISTED) != 0);
+#endif
+
+ /* Create the handshake layer. */
+ qc->tls = ossl_ssl_connection_new_int(ql->obj.ssl.ctx, NULL, TLS_method());
+ if (qc->tls == NULL || (sc = SSL_CONNECTION_FROM_SSL(qc->tls)) == NULL) {
+ QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_INTERNAL_ERROR, NULL);
+ goto err;
+ }
+ sc->s3.flags |= TLS1_FLAGS_QUIC;
+
+ qc->default_ssl_options = OSSL_QUIC_PERMITTED_OPTIONS;
+ qc->last_error = SSL_ERROR_NONE;
+
+ /*
+ * This is QCSO, we don't expect to accept connections
+ * on success the channel assumes ownership of tls, we need
+ * to grab reference for qc.
+ */
+ qc->ch = ossl_quic_port_create_outgoing(qc->port, qc->tls);
+
+ ossl_quic_channel_set_msg_callback(qc->ch, ql->obj.ssl.ctx->msg_callback, &qc->obj.ssl);
+ ossl_quic_channel_set_msg_callback_arg(qc->ch, ql->obj.ssl.ctx->msg_callback_arg);
+
+ /*
+ * We deliberately pass NULL for engine and port, because we don't want to
+ * to turn QCSO we create here into an event leader, nor port leader.
+ * Both those roles are occupied already by listener (`ssl`) we use
+ * to create a new QCSO here.
+ */
+ if (!ossl_quic_obj_init(&qc->obj, ql->obj.ssl.ctx,
+ SSL_TYPE_QUIC_CONNECTION,
+ &ql->obj.ssl, NULL, NULL)) {
+ QUIC_RAISE_NON_NORMAL_ERROR(NULL, ERR_R_INTERNAL_ERROR, NULL);
+ goto err;
+ }
+
+ /* Initialise libssl APL-related state. */
+ qc->default_stream_mode = SSL_DEFAULT_STREAM_MODE_AUTO_BIDI;
+ qc->default_ssl_mode = qc->obj.ssl.ctx->mode;
+ qc->default_ssl_options = qc->obj.ssl.ctx->options & OSSL_QUIC_PERMITTED_OPTIONS;
+ qc->incoming_stream_policy = SSL_INCOMING_STREAM_POLICY_AUTO;
+ qc->last_error = SSL_ERROR_NONE;
+
+ qc_update_reject_policy(qc);
+
+ qctx_unlock(&ctx);
+
+ return &qc->obj.ssl;
+
+err:
+ if (qc != NULL) {
+ qc_cleanup(qc, /* have_lock= */ 0);
+ OPENSSL_free(qc);
+ }
+ qctx_unlock(&ctx);
+ SSL_free(&ctx.ql->obj.ssl);
+
return NULL;
}
return testresult;
}
+static int select_alpn(SSL *ssl, const unsigned char **out,
+ unsigned char *out_len, const unsigned char *in,
+ unsigned int in_len, void *arg)
+{
+ static unsigned char alpn[] = { 8, 'o', 's', 's', 'l', 't', 'e', 's', 't' };
+
+ if (SSL_select_next_proto((unsigned char **)out, out_len, alpn, sizeof(alpn),
+ in, in_len) == OPENSSL_NPN_NEGOTIATED)
+ return SSL_TLSEXT_ERR_OK;
+ return SSL_TLSEXT_ERR_ALERT_FATAL;
+}
+
+static SSL_CTX *create_server_ctx(void)
+{
+ SSL_CTX *ssl_ctx;
+
+ if (!TEST_ptr(ssl_ctx = SSL_CTX_new_ex(libctx, NULL, OSSL_QUIC_server_method()))
+ || !TEST_true(SSL_CTX_use_certificate_file(ssl_ctx, cert, SSL_FILETYPE_PEM))
+ || !TEST_true(SSL_CTX_use_PrivateKey_file(ssl_ctx, privkey, SSL_FILETYPE_PEM))) {
+ SSL_CTX_free(ssl_ctx);
+ ssl_ctx = NULL;
+ } else {
+ SSL_CTX_set_alpn_select_cb(ssl_ctx, select_alpn, NULL);
+ SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_NONE, NULL);
+ }
+
+ return ssl_ctx;
+}
+
+static BIO_ADDR *create_addr(struct in_addr *ina, short int port)
+{
+ BIO_ADDR *addr = NULL;
+
+ if (!TEST_ptr(addr = BIO_ADDR_new()))
+ return NULL;
+
+ if (!TEST_true(BIO_ADDR_rawmake(addr, AF_INET, ina, sizeof(struct in_addr),
+ htons(port)))) {
+ BIO_ADDR_free(addr);
+ return NULL;
+ }
+
+ return addr;
+}
+
+static int bio_addr_bind(BIO *bio, BIO_ADDR *addr)
+{
+ int bio_caps = BIO_DGRAM_CAP_HANDLES_DST_ADDR | BIO_DGRAM_CAP_HANDLES_SRC_ADDR;
+
+ if (!TEST_true(BIO_dgram_set_caps(bio, bio_caps)))
+ return 0;
+
+ if (!TEST_int_eq(BIO_dgram_set0_local_addr(bio, addr), 1))
+ return 0;
+
+ return 1;
+}
+
+static SSL *ql_create(SSL_CTX *ssl_ctx, BIO *bio)
+{
+ SSL *qserver;
+
+ if (!TEST_ptr(qserver = SSL_new_listener(ssl_ctx, 0))) {
+ BIO_free(bio);
+ return NULL;
+ }
+
+ SSL_set_bio(qserver, bio, bio);
+
+ if (!TEST_true(SSL_listen(qserver))) {
+ SSL_free(qserver);
+ return NULL;
+ }
+
+ return qserver;
+}
+
+static int qc_init(SSL *qconn, BIO_ADDR *dst_addr)
+{
+ static unsigned char alpn[] = { 8, 'o', 's', 's', 'l', 't', 'e', 's', 't' };
+
+ if (!TEST_true(SSL_set1_initial_peer_addr(qconn, dst_addr)))
+ return 0;
+
+ if (!TEST_false(SSL_set_alpn_protos(qconn, alpn, sizeof(alpn))))
+ return 0;
+
+ return 1;
+}
+
+static int test_ssl_new_from_listener(void)
+{
+ SSL_CTX *lctx = NULL, *sctx = NULL;
+ SSL *qlistener = NULL, *qserver = NULL, *qconn = 0;
+ int testresult = 0;
+ int chk;
+ BIO *lbio = NULL, *sbio = NULL;
+ BIO_ADDR *addr = NULL;
+ struct in_addr ina;
+
+ ina.s_addr = htonl(0x1f000001);
+ if (!TEST_ptr(lctx = create_server_ctx())
+ || !TEST_ptr(sctx = create_server_ctx())
+ || !TEST_true(BIO_new_bio_dgram_pair(&lbio, 0, &sbio, 0)))
+ goto err;
+
+ if (!TEST_ptr(addr = create_addr(&ina, 8040)))
+ goto err;
+
+ if (!TEST_true(bio_addr_bind(lbio, addr)))
+ goto err;
+ addr = NULL;
+
+ if (!TEST_ptr(addr = create_addr(&ina, 4080)))
+ goto err;
+
+ if (!TEST_true(bio_addr_bind(sbio, addr)))
+ goto err;
+ addr = NULL;
+
+ qlistener = ql_create(lctx, lbio);
+ lbio = NULL;
+ if (!TEST_ptr(qlistener))
+ goto err;
+
+ qserver = ql_create(sctx, sbio);
+ sbio = NULL;
+ if (!TEST_ptr(qserver))
+ goto err;
+
+ if (!TEST_ptr(qconn = SSL_new_from_listener(qlistener, 0)))
+ goto err;
+
+ if (!TEST_ptr(addr = create_addr(&ina, 4080)))
+ goto err;
+
+ chk = qc_init(qconn, addr);
+ if (!TEST_true(chk))
+ goto err;
+
+ while ((chk = SSL_do_handshake(qconn)) == -1) {
+ SSL_handle_events(qserver);
+ SSL_handle_events(qlistener);
+ }
+
+ if (!TEST_int_gt(chk, 0)) {
+ TEST_info("SSL_do_handshake() failed\n");
+ goto err;
+ }
+
+ testresult = 1;
+ err:
+ SSL_free(qconn);
+ SSL_free(qlistener);
+ SSL_free(qserver);
+ BIO_free(lbio);
+ BIO_free(sbio);
+ SSL_CTX_free(sctx);
+ SSL_CTX_free(lctx);
+ BIO_ADDR_free(addr);
+
+ return testresult;
+}
+
/***********************************************************************************/
OPT_TEST_DECLARE_USAGE("provider config certsdir datadir\n")
ADD_TEST(test_session_cb);
ADD_TEST(test_domain_flags);
ADD_TEST(test_early_ticks);
+ ADD_TEST(test_ssl_new_from_listener);
return 1;
err:
cleanup_tests();