diff options
Diffstat (limited to 'test/radix/quic_ops.c')
-rw-r--r-- | test/radix/quic_ops.c | 1044 |
1 files changed, 1044 insertions, 0 deletions
diff --git a/test/radix/quic_ops.c b/test/radix/quic_ops.c new file mode 100644 index 0000000000..f3dedc13ac --- /dev/null +++ b/test/radix/quic_ops.c @@ -0,0 +1,1044 @@ +#include <netinet/in.h> + +static const unsigned char alpn_ossltest[] = { + /* "\x08ossltest" (hex for EBCDIC resilience) */ + 0x08, 0x6f, 0x73, 0x73, 0x6c, 0x74, 0x65, 0x73, 0x74 +}; + +DEF_FUNC(hf_unbind) +{ + int ok = 0; + const char *name; + + F_POP(name); + RADIX_PROCESS_set_obj(RP(), name, NULL); + + ok = 1; +err: + return ok; +} + +static int ssl_ctx_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_ALERT_FATAL; + + return SSL_TLSEXT_ERR_OK; +} + +static int ssl_ctx_configure(SSL_CTX *ctx, int is_server) +{ + if (!TEST_true(ossl_quic_set_diag_title(ctx, "quic_radix_test"))) + return 0; + + if (!is_server) + return 1; + + if (!TEST_int_eq(SSL_CTX_use_certificate_file(ctx, cert_file, + SSL_FILETYPE_PEM), 1) + || !TEST_int_eq(SSL_CTX_use_PrivateKey_file(ctx, key_file, + SSL_FILETYPE_PEM), 1)) + return 0; + + SSL_CTX_set_alpn_select_cb(ctx, ssl_ctx_select_alpn, NULL); + return 1; +} + +static int ssl_create_bound_socket(uint16_t listen_port, + int *p_fd, uint16_t *p_result_port) +{ + int ok = 0; + int fd = -1; + BIO_ADDR *addr = NULL; + union BIO_sock_info_u info; + struct in_addr ina = { htonl(INADDR_LOOPBACK) }; + + fd = BIO_socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP, 0); + if (!TEST_int_ge(fd, 0)) + goto err; + + if (!TEST_true(BIO_socket_nbio(fd, 1))) + goto err; + + if (!TEST_ptr(addr = BIO_ADDR_new())) + goto err; + + if (!TEST_true(BIO_ADDR_rawmake(addr, AF_INET, + &ina, sizeof(ina), 0))) + goto err; + + if (!TEST_true(BIO_bind(fd, addr, 0))) + goto err; + + info.addr = addr; + if (!TEST_true(BIO_sock_info(fd, BIO_SOCK_INFO_ADDRESS, &info))) + goto err; + + if (!TEST_int_gt(BIO_ADDR_rawport(addr), 0)) + goto err; + + ok = 1; +err: + if (!ok && fd >= 0) + BIO_closesocket(fd); + else if (ok) { + *p_fd = fd; + if (p_result_port != NULL) + *p_result_port = BIO_ADDR_rawport(addr); + } + BIO_ADDR_free(addr); + return ok; +} + +static int ssl_attach_bio_dgram(SSL *ssl, + uint16_t local_port, uint16_t *actual_port) +{ + int s_fd = -1; + BIO *bio; + + if (!TEST_true(ssl_create_bound_socket(local_port, &s_fd, actual_port))) + return 0; + + if (!TEST_ptr(bio = BIO_new_dgram(s_fd, BIO_CLOSE))) { + BIO_closesocket(s_fd); + return 0; + } + + SSL_set0_rbio(ssl, bio); + if (!TEST_true(BIO_up_ref(bio))) + return 0; + + SSL_set0_wbio(ssl, bio); + + return 1; +} + +DEF_FUNC(hf_new_ssl) +{ + int ok = 0; + const char *name; + SSL_CTX *ctx = NULL; + const SSL_METHOD *method; + SSL *ssl; + uint64_t flags; + int is_server; + + F_POP2(name, flags); + + is_server = (flags != 0); + method = is_server ? OSSL_QUIC_server_method() : OSSL_QUIC_client_method(); + if (!TEST_ptr(ctx = SSL_CTX_new(method))) + goto err; + + if (!TEST_true(ssl_ctx_configure(ctx, is_server))) + goto err; + + if (is_server) { + if (!TEST_ptr(ssl = SSL_new_listener(ctx, 0))) + goto err; + } else { + if (!TEST_ptr(ssl = SSL_new(ctx))) + goto err; + } + + if (!TEST_true(ssl_attach_bio_dgram(ssl, 0, NULL))) + goto err; + + if (!TEST_true(RADIX_PROCESS_set_ssl(RP(), name, ssl))) { + SSL_free(ssl); + goto err; + } + + ok = 1; +err: + /* SSL object will hold ref, we don't need it */ + SSL_CTX_free(ctx); + return ok; +} + +DEF_FUNC(hf_listen) +{ + int ok = 0, r; + SSL *ssl; + + REQUIRE_SSL(ssl); + + r = SSL_listen(ssl); + if (!TEST_true(r)) + goto err; + + radix_activate_slot(0); + ok = 1; +err: + return ok; +} + +DEF_FUNC(hf_new_stream) +{ + int ok = 0; + const char *conn_name, *stream_name; + SSL *conn, *stream; + uint64_t flags, do_accept; + + F_POP2(flags, do_accept); + F_POP2(conn_name, stream_name); + + if (!TEST_ptr_null(RADIX_PROCESS_get_obj(RP(), stream_name))) + goto err; + + if (!TEST_ptr(conn = RADIX_PROCESS_get_ssl(RP(), conn_name))) + goto err; + + if (do_accept) { + stream = SSL_accept_stream(conn, flags); + + if (stream == NULL) + F_SPIN_AGAIN(); + } else { + stream = SSL_new_stream(conn, flags); + } + + if (!TEST_ptr(stream)) + goto err; + + /* TODO(QUIC RADIX): Implement wait behaviour */ + + if (stream != NULL + && !TEST_true(RADIX_PROCESS_set_ssl(RP(), stream_name, stream))) { + SSL_free(stream); + goto err; + } + + ok = 1; +err: + return ok; +} + +DEF_FUNC(hf_accept_conn) +{ + int ok = 0; + const char *conn_name; + uint64_t flags; + SSL *listener, *conn; + + F_POP2(conn_name, flags); + REQUIRE_SSL(listener); + + if (!TEST_ptr_null(RADIX_PROCESS_get_obj(RP(), conn_name))) + goto err; + + conn = SSL_accept_connection(listener, flags); + if (conn == NULL) + F_SPIN_AGAIN(); + + if (!TEST_true(RADIX_PROCESS_set_ssl(RP(), conn_name, conn))) { + SSL_free(conn); + goto err; + } + + ok = 1; +err: + return ok; +} + +DEF_FUNC(hf_accept_conn_none) +{ + int ok = 0; + SSL *listener, *conn; + + REQUIRE_SSL(listener); + + conn = SSL_accept_connection(listener, 0); + if (!TEST_ptr_null(conn)) { + SSL_free(conn); + goto err; + } + + ok = 1; +err: + return ok; +} + +DEF_FUNC(hf_accept_stream_none) +{ + int ok = 0; + const char *conn_name; + uint64_t flags; + SSL *conn, *stream; + + F_POP2(conn_name, flags); + + if (!TEST_ptr(conn = RADIX_PROCESS_get_ssl(RP(), conn_name))) + goto err; + + stream = SSL_accept_stream(conn, flags); + if (!TEST_ptr_null(stream)) { + SSL_free(stream); + goto err; + } + + ok = 1; +err: + return ok; +} + +DEF_FUNC(hf_pop_err) +{ + ERR_pop(); + + return 1; +} + +DEF_FUNC(hf_stream_reset) +{ + int ok = 0; + const char *name; + SSL_STREAM_RESET_ARGS args = {0}; + SSL *ssl; + + F_POP2(name, args.quic_error_code); + REQUIRE_SSL(ssl); + + if (!TEST_true(SSL_stream_reset(ssl, &args, sizeof(args)))) + goto err; + + ok = 1; +err: + return ok; +} + +DEF_FUNC(hf_set_default_stream_mode) +{ + int ok = 0; + uint64_t mode; + SSL *ssl; + + F_POP(mode); + REQUIRE_SSL(ssl); + + if (!TEST_true(SSL_set_default_stream_mode(ssl, mode))) + goto err; + + ok = 1; +err: + return ok; +} + +DEF_FUNC(hf_set_incoming_stream_policy) +{ + int ok = 0; + uint64_t policy, error_code; + SSL *ssl; + + F_POP(error_code); + F_POP(policy); + REQUIRE_SSL(ssl); + + if (!TEST_true(SSL_set_incoming_stream_policy(ssl, policy, error_code))) + goto err; + + ok = 1; +err: + return ok; +} + +DEF_FUNC(hf_shutdown_wait) +{ + int ok = 0, ret; + uint64_t flags; + SSL *ssl; + SSL_SHUTDOWN_EX_ARGS args = {0}; + QUIC_CHANNEL *ch; + + F_POP(args.quic_reason); + F_POP(args.quic_error_code); + F_POP(flags); + REQUIRE_SSL(ssl); + + ch = ossl_quic_conn_get_channel(ssl); + ossl_quic_engine_set_inhibit_tick(ossl_quic_channel_get0_engine(ch), 0); + + ret = SSL_shutdown_ex(ssl, flags, &args, sizeof(args)); + if (!TEST_int_ge(ret, 0)) + goto err; + + if (ret == 0) + F_SPIN_AGAIN(); + + ok = 1; +err: + return ok; +} + +DEF_FUNC(hf_conclude) +{ + int ok = 0; + SSL *ssl; + + REQUIRE_SSL(ssl); + + if (!TEST_true(SSL_stream_conclude(ssl, 0))) + goto err; + + ok = 1; +err: + return ok; +} + +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 check_consistent_want(SSL *s, int ret) +{ + int ec = SSL_get_error(s, ret); + int w = SSL_want(s); + + int ok = TEST_true( + (ec == SSL_ERROR_NONE && w == SSL_NOTHING) + || (ec == SSL_ERROR_ZERO_RETURN && w == SSL_NOTHING) + || (ec == SSL_ERROR_SSL && w == SSL_NOTHING) + || (ec == SSL_ERROR_SYSCALL && w == SSL_NOTHING) + || (ec == SSL_ERROR_WANT_READ && w == SSL_READING) + || (ec == SSL_ERROR_WANT_WRITE && w == SSL_WRITING) + || (ec == SSL_ERROR_WANT_CLIENT_HELLO_CB && w == SSL_CLIENT_HELLO_CB) + || (ec == SSL_ERROR_WANT_X509_LOOKUP && w == SSL_X509_LOOKUP) + || (ec == SSL_ERROR_WANT_RETRY_VERIFY && w == SSL_RETRY_VERIFY) + ); + + if (!ok) + TEST_error("got error=%d, want=%d", ec, w); + + return ok; +} + +DEF_FUNC(hf_write) +{ + int ok = 0, r; + SSL *ssl; + const void *buf; + size_t buf_len, bytes_written = 0; + + F_POP2(buf, buf_len); + REQUIRE_SSL(ssl); + + r = SSL_write_ex(ssl, buf, buf_len, &bytes_written); + if (!TEST_true(r) + || !check_consistent_want(ssl, r) + || !TEST_size_t_eq(bytes_written, buf_len)) + goto err; + + ok = 1; +err: + return ok; +} + +DEF_FUNC(hf_write_ex2) +{ + int ok = 0, r; + SSL *ssl; + const void *buf; + size_t buf_len, bytes_written = 0; + uint64_t flags; + + F_POP(flags); + F_POP2(buf, buf_len); + REQUIRE_SSL(ssl); + + r = SSL_write_ex2(ssl, buf, buf_len, flags, &bytes_written); + if (!TEST_true(r) + || !check_consistent_want(ssl, r) + || !TEST_size_t_eq(bytes_written, buf_len)) + goto err; + + ok = 1; +err: + return ok; +} + +DEF_FUNC(hf_write_fail) +{ + int ok = 0, ret; + SSL *ssl; + size_t bytes_written = 0; + + REQUIRE_SSL(ssl); + + ret = SSL_write_ex(ssl, "apple", 5, &bytes_written); + if (!TEST_false(ret) + || !TEST_true(check_consistent_want(ssl, ret)) + || !TEST_size_t_eq(bytes_written, 0)) + goto err; + + ok = 1; +err: + return ok; +} + +DEF_FUNC(hf_read_expect) +{ + int ok = 0, r; + SSL *ssl; + const void *buf; + uint64_t buf_len, bytes_read = 0; + + F_POP2(buf, buf_len); + REQUIRE_SSL(ssl); + + if (buf_len > 0 && RT()->tmp_buf == NULL + && !TEST_ptr(RT()->tmp_buf = OPENSSL_malloc(buf_len))) + goto err; + + r = SSL_read_ex(ssl, RT()->tmp_buf + RT()->tmp_buf_offset, + buf_len - RT()->tmp_buf_offset, + &bytes_read); + if (!TEST_true(check_consistent_want(ssl, r))) + goto err; + + if (!r) + F_SPIN_AGAIN(); + + if (bytes_read + RT()->tmp_buf_offset != buf_len) { + RT()->tmp_buf_offset += bytes_read; + F_SPIN_AGAIN(); + } + + if (buf_len > 0 + && !TEST_mem_eq(RT()->tmp_buf, buf_len, buf, buf_len)) + goto err; + + OPENSSL_free(RT()->tmp_buf); + RT()->tmp_buf = NULL; + RT()->tmp_buf_offset = 0; + + ok = 1; +err: + return ok; +} + +DEF_FUNC(hf_read_fail) +{ + int ok = 0, r; + SSL *ssl; + char buf[1] = {0}; + size_t bytes_read = 0; + uint64_t do_wait; + + F_POP(do_wait); + REQUIRE_SSL(ssl); + + r = SSL_read_ex(ssl, buf, sizeof(buf), &bytes_read); + if (!TEST_false(r) + || !TEST_true(check_consistent_want(ssl, r)) + || !TEST_size_t_eq(bytes_read, 0)) + goto err; + + if (do_wait && is_want(ssl, 0)) + F_SPIN_AGAIN(); + + ok = 1; +err: + return ok; +} + +DEF_FUNC(hf_connect_wait) +{ + int ok = 0, ret; + SSL *ssl; + + REQUIRE_SSL(ssl); + + /* if not started */ + if (RT()->scratch0 == 0) { + if (!TEST_true(SSL_set_blocking_mode(ssl, 0))) + return 0; + + SSL_CONN_CLOSE_INFO cc_info = {0}; + if (!TEST_false(SSL_get_conn_close_info(ssl, &cc_info, sizeof(cc_info)))) + goto err; + + /* 0 is the success case for SSL_set_alpn_protos(). */ + if (!TEST_false(SSL_set_alpn_protos(ssl, alpn_ossltest, + sizeof(alpn_ossltest)))) + goto err; + } + + RT()->scratch0 = 1; /* connect started */ + ret = SSL_connect(ssl); + radix_activate_slot(0); + if (!TEST_true(check_consistent_want(ssl, ret))) + goto err; + + if (ret != 1) { + if (1 /* TODO */ && is_want(ssl, ret)) + F_SPIN_AGAIN(); + + if (!TEST_int_eq(ret, 1)) + goto err; + } + + ok = 1; +err: + RT()->scratch0 = 0; + return ok; +} + +DEF_FUNC(hf_detach) +{ + int ok = 0; + const char *conn_name, *stream_name; + SSL *conn, *stream; + + F_POP2(conn_name, stream_name); + if (!TEST_ptr(conn = RADIX_PROCESS_get_ssl(RP(), conn_name))) + goto err; + + if (!TEST_ptr(stream = ossl_quic_detach_stream(conn))) + goto err; + + if (!TEST_true(RADIX_PROCESS_set_ssl(RP(), stream_name, stream))) { + SSL_free(stream); + goto err; + } + + ok = 1; +err: + return ok; +} + +DEF_FUNC(hf_attach) +{ + int ok = 0; + const char *conn_name, *stream_name; + SSL *conn, *stream; + + F_POP2(conn_name, stream_name); + + if (!TEST_ptr(conn = RADIX_PROCESS_get_ssl(RP(), conn_name))) + goto err; + + if (!TEST_ptr(stream = RADIX_PROCESS_get_ssl(RP(), stream_name))) + goto err; + + if (!TEST_true(ossl_quic_attach_stream(conn, stream))) + goto err; + + if (!TEST_true(RADIX_PROCESS_set_ssl(RP(), stream_name, NULL))) + goto err; + + ok = 1; +err: + return ok; +} + +DEF_FUNC(hf_expect_fin) +{ + int ok = 0, ret; + SSL *ssl; + char buf[1]; + size_t bytes_read = 0; + + REQUIRE_SSL(ssl); + + ret = SSL_read_ex(ssl, buf, sizeof(buf), &bytes_read); + if (!TEST_true(check_consistent_want(ssl, ret)) + || !TEST_false(ret) + || !TEST_size_t_eq(bytes_read, 0)) + goto err; + + if (is_want(ssl, 0)) + F_SPIN_AGAIN(); + + if (!TEST_int_eq(SSL_get_error(ssl, 0), + SSL_ERROR_ZERO_RETURN)) + goto err; + + if (!TEST_int_eq(SSL_want(ssl), SSL_NOTHING)) + goto err; + + ok = 1; +err: + return ok; +} + +DEF_FUNC(hf_expect_conn_close_info) +{ + int ok = 0; + SSL *ssl; + SSL_CONN_CLOSE_INFO cc_info = {0}; + uint64_t error_code, expect_app, expect_remote; + + F_POP(error_code); + F_POP2(expect_app, expect_remote); + REQUIRE_SSL(ssl); + + /* TODO BLOCKING */ + + if (!SSL_get_conn_close_info(ssl, &cc_info, sizeof(cc_info))) + F_SPIN_AGAIN(); + + if (!TEST_int_eq(expect_app, + (cc_info.flags & SSL_CONN_CLOSE_FLAG_TRANSPORT) == 0) + || !TEST_int_eq(expect_remote, + (cc_info.flags & SSL_CONN_CLOSE_FLAG_LOCAL) == 0) + || !TEST_uint64_t_eq(error_code, cc_info.error_code)) { + TEST_info("connection close reason: %s", cc_info.reason); + goto err; + } + + ok = 1; +err: + return ok; +} + +DEF_FUNC(hf_wait_for_data) +{ + int ok = 0; + SSL *ssl; + char buf[1]; + size_t bytes_read = 0; + + REQUIRE_SSL(ssl); + + if (!SSL_peek_ex(ssl, buf, sizeof(buf), &bytes_read) + || bytes_read == 0) + F_SPIN_AGAIN(); + + ok = 1; +err: + return ok; +} + +DEF_FUNC(hf_expect_err) +{ + int ok = 0; + uint64_t lib, reason; + + F_POP2(lib, reason); + if (!TEST_size_t_eq((size_t)ERR_GET_LIB(ERR_peek_last_error()), lib) + || !TEST_size_t_eq((size_t)ERR_GET_REASON(ERR_peek_last_error()), reason)) + goto err; + + ok = 1; +err: + return ok; +} + +DEF_FUNC(hf_expect_ssl_err) +{ + int ok = 0; + uint64_t expected; + SSL *ssl; + + F_POP(expected); + REQUIRE_SSL(ssl); + + if (!TEST_size_t_eq((size_t)SSL_get_error(ssl, 0), expected) + || !TEST_int_eq(SSL_want(ssl), SSL_NOTHING)) + goto err; + + ok = 1; +err: + return ok; +} + +DEF_FUNC(hf_expect_stream_id) +{ + int ok = 0; + SSL *ssl; + uint64_t expected, actual; + + F_POP(expected); + REQUIRE_SSL(ssl); + + actual = SSL_get_stream_id(ssl); + if (!TEST_uint64_t_eq(actual, expected)) + goto err; + + ok = 1; +err: + return ok; +} + +DEF_FUNC(hf_select_ssl) +{ + int ok = 0; + uint64_t slot; + const char *name; + RADIX_OBJ *obj; + + F_POP2(slot, name); + if (!TEST_ptr(obj = RADIX_PROCESS_get_obj(RP(), name))) + goto err; + + if (!TEST_uint64_t_lt(slot, NUM_SLOTS)) + goto err; + + RT()->slot[slot] = obj; + RT()->ssl[slot] = obj->ssl; + ok = 1; +err: + return ok; +} + +DEF_FUNC(hf_clear_slot) +{ + int ok = 0; + uint64_t slot; + + F_POP(slot); + if (!TEST_uint64_t_lt(slot, NUM_SLOTS)) + goto err; + + RT()->slot[slot] = NULL; + RT()->ssl[slot] = NULL; + ok = 1; +err: + return ok; +} + +DEF_FUNC(hf_skip_time) +{ + int ok = 0; + uint64_t ms; + + F_POP(ms); + + radix_skip_time(ossl_ms2time(ms)); + ok = 1; +err: + return ok; +} + +DEF_FUNC(hf_set_peer_addr_from) +{ + int ok = 0; + SSL *dst_ssl, *src_ssl; + BIO *dst_bio, *src_bio; + int src_fd = -1; + union BIO_sock_info_u src_info; + BIO_ADDR *src_addr = NULL; + + REQUIRE_SSL_N(0, dst_ssl); + REQUIRE_SSL_N(1, src_ssl); + dst_bio = SSL_get_rbio(dst_ssl); + src_bio = SSL_get_rbio(src_ssl); + if (!TEST_ptr(dst_bio) || !TEST_ptr(src_bio)) + goto err; + + if (!TEST_ptr(src_addr = BIO_ADDR_new())) + goto err; + + if (!TEST_true(BIO_get_fd(src_bio, &src_fd)) + || !TEST_int_ge(src_fd, 0)) + goto err; + + src_info.addr = src_addr; + if (!TEST_true(BIO_sock_info(src_fd, BIO_SOCK_INFO_ADDRESS, &src_info)) + || !TEST_int_ge(ntohs(BIO_ADDR_rawport(src_addr)), 0)) + goto err; + + /* + * Could use SSL_set_initial_peer_addr here, but set it on the + * BIO_s_datagram instead and make sure we pick it up automatically. + */ + if (!TEST_true(BIO_dgram_set_peer(dst_bio, src_addr))) + goto err; + + ok = 1; +err: + BIO_ADDR_free(src_addr); + return ok; +} + +#define OP_UNBIND(name) \ + (OP_PUSH_PZ(#name), \ + OP_FUNC(hf_unbind)) + +#define OP_SELECT_SSL(slot, name) \ + (OP_PUSH_U64(slot), \ + OP_PUSH_PZ(#name), \ + OP_FUNC(hf_select_ssl)) + +#define OP_CLEAR_SLOT(slot) \ + (OP_PUSH_U64(slot), \ + OP_FUNC(hf_clear_slot)) + +#define OP_CONNECT_WAIT(name) \ + (OP_SELECT_SSL(0, name), \ + OP_FUNC(hf_connect_wait)) + +#define OP_LISTEN(name) \ + (OP_SELECT_SSL(0, name), \ + OP_FUNC(hf_listen)) + +#define OP_NEW_SSL_C(name) \ + (OP_PUSH_PZ(#name), \ + OP_PUSH_U64(0), \ + OP_FUNC(hf_new_ssl)) + +#define OP_NEW_SSL_L(name) \ + (OP_PUSH_PZ(#name), \ + OP_PUSH_U64(1), \ + OP_FUNC(hf_new_ssl)) + +#define OP_NEW_SSL_L_LISTEN(name) \ + (OP_NEW_SSL_L(name), \ + OP_LISTEN(name)) + +#define OP_SET_PEER_ADDR_FROM(dst_name, src_name) \ + (OP_SELECT_SSL(0, dst_name), \ + OP_SELECT_SSL(1, src_name), \ + OP_FUNC(hf_set_peer_addr_from)) + +#define OP_SIMPLE_PAIR_CONN() \ + (OP_NEW_SSL_L_LISTEN(L), \ + OP_NEW_SSL_C(C), \ + OP_SET_PEER_ADDR_FROM(C, L), \ + OP_CONNECT_WAIT(C)) + +#define OP_NEW_STREAM(conn_name, stream_name, flags) \ + (OP_SELECT_SSL(0, conn_name), \ + OP_PUSH_PZ(#stream_name), \ + OP_PUSH_U64(flags), \ + OP_PUSH_U64(0), \ + OP_FUNC(hf_new_stream)) + +#define OP_ACCEPT_STREAM_WAIT(conn_name, stream_name, flags) \ + (OP_SELECT_SSL(0, conn_name), \ + OP_PUSH_PZ(#stream_name), \ + OP_PUSH_U64(flags), \ + OP_PUSH_U64(1), \ + OP_FUNC(hf_new_stream)) + +#define OP_ACCEPT_STREAM_NONE(conn_name) \ + (OP_SELECT_SSL(0, conn_name), \ + OP_FUNC(hf_accept_stream_none)) + +#define OP_ACCEPT_CONN_WAIT(listener_name, conn_name, flags) \ + (OP_SELECT_SSL(0, listener_name), \ + OP_PUSH_PZ(#conn_name), \ + OP_PUSH_U64(flags), \ + OP_FUNC(hf_accept_conn)) + +#define OP_ACCEPT_CONN_NONE(listener_name) \ + (OP_SELECT_SSL(0, listener_name), \ + OP_FUNC(hf_accept_conn_none)) + +#define OP_WRITE(name, buf, buf_len) \ + (OP_SELECT_SSL(0, name), \ + OP_PUSH_BUFP(buf, buf_len), \ + OP_FUNC(hf_write)) + +#define OP_WRITE_B(name, buf) \ + OP_WRITE(name, (buf), sizeof(buf)) + +#define OP_WRITE_EX2(name, buf, buf_len, flags) \ + (OP_SELECT_SSL(0, name), \ + OP_PUSH_BUFP(buf, buf_len), \ + OP_PUSH_U64(flags), \ + OP_FUNC(hf_write_ex2)) + +#define OP_WRITE_FAIL(name) \ + (OP_SELECT_SSL(0, name), \ + OP_FUNC(hf_write_fail)) + +#define OP_CONCLUDE(name) \ + (OP_SELECT_SSL(0, name), \ + OP_FUNC(hf_conclude)) + +#define OP_READ_EXPECT(name, buf, buf_len) \ + (OP_SELECT_SSL(0, name), \ + OP_PUSH_BUFP(buf, buf_len), \ + OP_FUNC(hf_read_expect)) + +#define OP_READ_EXPECT_B(name, buf) \ + OP_READ_EXPECT(name, (buf), sizeof(buf)) + +#define OP_READ_FAIL() \ + (OP_SELECT_SSL(0, name), \ + OP_PUSH_U64(0), \ + OP_FUNC(hf_read_fail)) + +#define OP_READ_FAIL_WAIT(name) \ + (OP_SELECT_SSL(0, name), \ + OP_PUSH_U64(1), \ + OP_FUNC(hf_read_fail) + +#define OP_POP_ERR() \ + OP_FUNC(hf_pop_err) + +#define OP_SET_DEFAULT_STREAM_MODE(name, mode) \ + (OP_SELECT_SSL(0, name), \ + OP_PUSH_U64(mode), \ + OP_FUNC(hf_set_default_stream_mode)) + +#define OP_SET_INCOMING_STREAM_POLICY(name, policy, error_code) \ + (OP_SELECT_SSL(0, name), \ + OP_PUSH_U64(policy), \ + OP_PUSH_U64(error_code), \ + OP_FUNC(hf_set_incoming_stream_policy)) + +#define OP_STREAM_RESET(name, error_code) \ + (OP_SELECT_SSL(0, name), \ + OP_PUSH_U64(flags), \ + OP_PUSH_U64(error_code), \ + OP_FUNC(hf_stream_reset)) \ + +#define OP_SHUTDOWN_WAIT(name, flags, error_code, reason) \ + (OP_SELECT_SSL(0, name), \ + OP_PUSH_U64(flags), \ + OP_PUSH_U64(error_code), \ + OP_PUSH_PZ(reason), \ + OP_FUNC(hf_shutdown_wait)) + +#define OP_DETACH(conn_name, stream_name) \ + (OP_SELECT_SSL(0, conn_name), \ + OP_PUSH_PZ(#stream_name), \ + OP_FUNC(hf_detach)) + +#define OP_ATTACH(conn_name, stream_name) \ + (OP_SELECT_SSL(0, conn_name), \ + OP_PUSH_PZ(stream_name), \ + OP_FUNC(hf_attach)) + +#define OP_EXPECT_FIN(name) \ + (OP_SELECT_SSL(0, name), \ + OP_FUNC(hf_expect_fin)) + +#define OP_EXPECT_CONN_CLOSE_INFO(name, error_code, expect_app, expect_remote) \ + (OP_SELECT_SSL(0, name), \ + OP_PUSH_U64(expect_app), \ + OP_PUSH_U64(expect_remote), \ + OP_PUSH_U64(error_code), \ + OP_FUNC(hf_expect_conn_close_info)) + +#define OP_WAIT_FOR_DATA(name) \ + (OP_SELECT_SSL(0, name), \ + OP_FUNC(hf_wait_for_data)) + +#define OP_EXPECT_ERR(lib, reason) \ + (OP_PUSH_U64(lib), \ + OP_PUSH_U64(reason), \ + OP_FUNC(hf_expect_err)) + +#define OP_EXPECT_SSL_ERR(name, expected) \ + (OP_SELECT_SSL(0, name), \ + OP_PUSH_U64(expected), \ + OP_FUNC(hf_expect_ssl_err)) + +#define OP_EXPECT_STREAM_ID(expected) \ + (OP_PUSH_U64(expected), \ + OP_FUNC(hf_expect_stream_id)) + +#define OP_SKIP_TIME(ms) \ + (OP_PUSH_U64(ms), \ + OP_FUNC(hf_skip_time)) |