diff options
Diffstat (limited to 'tests')
42 files changed, 17842 insertions, 0 deletions
diff --git a/tests/.gitignore b/tests/.gitignore new file mode 100644 index 0000000..ba2906d --- /dev/null +++ b/tests/.gitignore @@ -0,0 +1 @@ +main diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..db09b7b --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,68 @@ +# ngtcp2 + +# Copyright (c) 2016 ngtcp2 contributors +# Copyright (c) 2012 nghttp2 contributors + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +if(HAVE_CUNIT) + include_directories( + "${CMAKE_SOURCE_DIR}/lib" + "${CMAKE_SOURCE_DIR}/lib/includes" + "${CMAKE_BINARY_DIR}/lib/includes" + ${CUNIT_INCLUDE_DIRS} + ) + + set(main_SOURCES + main.c + ngtcp2_pkt_test.c + ngtcp2_range_test.c + ngtcp2_rob_test.c + ngtcp2_acktr_test.c + ngtcp2_map_test.c + ngtcp2_crypto_test.c + ngtcp2_rtb_test.c + ngtcp2_idtr_test.c + ngtcp2_conn_test.c + ngtcp2_ringbuf_test.c + ngtcp2_conv_test.c + ngtcp2_test_helper.c + ngtcp2_ksl_test.c + ngtcp2_gaptr_test.c + ngtcp2_vec_test.c + ngtcp2_strm_test.c + ngtcp2_pv_test.c + ngtcp2_pmtud_test.c + ngtcp2_str_test.c + ) + + add_executable(main EXCLUDE_FROM_ALL + ${main_SOURCES} + ) + target_include_directories(main PRIVATE ${CUNIT_INCLUDE_DIRS}) + # FIXME enable and fix warnings + #set_target_properties(main PROPERTIES COMPILE_FLAGS "${WARNCFLAGS}") + target_link_libraries(main + ngtcp2_static + ${CUNIT_LIBRARIES} + ) + add_test(main main) + add_dependencies(check main) +endif() diff --git a/tests/Makefile.am b/tests/Makefile.am new file mode 100644 index 0000000..3e21c3b --- /dev/null +++ b/tests/Makefile.am @@ -0,0 +1,94 @@ +# ngtcp2 + +# Copyright (c) 2016 ngtcp2 contributors +# Copyright (c) 2012 nghttp2 contributors + +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: + +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. + +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +EXTRA_DIST = CMakeLists.txt + +if HAVE_CUNIT + +check_PROGRAMS = main + +OBJECTS = \ + main.c \ + ngtcp2_pkt_test.c \ + ngtcp2_range_test.c \ + ngtcp2_rob_test.c \ + ngtcp2_acktr_test.c \ + ngtcp2_map_test.c \ + ngtcp2_crypto_test.c \ + ngtcp2_rtb_test.c \ + ngtcp2_idtr_test.c \ + ngtcp2_conn_test.c \ + ngtcp2_ringbuf_test.c \ + ngtcp2_conv_test.c \ + ngtcp2_ksl_test.c \ + ngtcp2_gaptr_test.c \ + ngtcp2_vec_test.c \ + ngtcp2_strm_test.c \ + ngtcp2_pv_test.c \ + ngtcp2_pmtud_test.c \ + ngtcp2_str_test.c \ + ngtcp2_test_helper.c +HFILES= \ + ngtcp2_pkt_test.h \ + ngtcp2_range_test.h \ + ngtcp2_rob_test.h \ + ngtcp2_acktr_test.h \ + ngtcp2_map_test.h \ + ngtcp2_crypto_test.h \ + ngtcp2_rtb_test.h \ + ngtcp2_idtr_test.h \ + ngtcp2_conn_test.h \ + ngtcp2_ringbuf_test.h \ + ngtcp2_conv_test.h \ + ngtcp2_ksl_test.h \ + ngtcp2_gaptr_test.h \ + ngtcp2_vec_test.h \ + ngtcp2_strm_test.h \ + ngtcp2_pv_test.h \ + ngtcp2_pmtud_test.h \ + ngtcp2_str_test.h \ + ngtcp2_test_helper.h + +main_SOURCES = $(HFILES) $(OBJECTS) + +# With static lib disabled and symbol hiding enabled, we have to link object +# files directly because the tests use symbols not included in public API. +if ENABLE_SHARED +main_LDADD = ${top_builddir}/lib/.libs/*.o +else +main_LDADD = ${top_builddir}/lib/.libs/libngtcp2.la +endif +main_LDADD += @CUNIT_LIBS@ +main_LDFLAGS = -static + +AM_CFLAGS = $(WARNCFLAGS) \ + -I${top_srcdir}/lib \ + -I${top_srcdir}/lib/includes \ + -I${top_builddir}/lib/includes \ + -DBUILDING_NGTCP2 \ + @CUNIT_CFLAGS@ @DEFS@ +AM_LDFLAGS = -no-install + +TESTS = main + +endif # HAVE_CUNIT diff --git a/tests/main.c b/tests/main.c new file mode 100644 index 0000000..d6cd17c --- /dev/null +++ b/tests/main.c @@ -0,0 +1,357 @@ +/* + * ngtcp2 + * + * Copyright (c) 2016 ngtcp2 contributors + * Copyright (c) 2012 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <stdio.h> +#include <string.h> +#include <CUnit/Basic.h> +/* include test cases' include files here */ +#include "ngtcp2_pkt_test.h" +#include "ngtcp2_range_test.h" +#include "ngtcp2_rob_test.h" +#include "ngtcp2_rtb_test.h" +#include "ngtcp2_acktr_test.h" +#include "ngtcp2_crypto_test.h" +#include "ngtcp2_idtr_test.h" +#include "ngtcp2_conn_test.h" +#include "ngtcp2_ringbuf_test.h" +#include "ngtcp2_conv_test.h" +#include "ngtcp2_ksl_test.h" +#include "ngtcp2_map_test.h" +#include "ngtcp2_gaptr_test.h" +#include "ngtcp2_vec_test.h" +#include "ngtcp2_strm_test.h" +#include "ngtcp2_pv_test.h" +#include "ngtcp2_pmtud_test.h" +#include "ngtcp2_str_test.h" + +static int init_suite1(void) { return 0; } + +static int clean_suite1(void) { return 0; } + +int main(void) { + CU_pSuite pSuite = NULL; + unsigned int num_tests_failed; + + /* initialize the CUnit test registry */ + if (CUE_SUCCESS != CU_initialize_registry()) + return (int)CU_get_error(); + + /* add a suite to the registry */ + pSuite = CU_add_suite("libngtcp2_TestSuite", init_suite1, clean_suite1); + if (NULL == pSuite) { + CU_cleanup_registry(); + return (int)CU_get_error(); + } + + init_static_path(); + + /* add the tests to the suite */ + if (!CU_add_test(pSuite, "pkt_decode_version_cid", + test_ngtcp2_pkt_decode_version_cid) || + !CU_add_test(pSuite, "pkt_decode_hd_long", + test_ngtcp2_pkt_decode_hd_long) || + !CU_add_test(pSuite, "pkt_decode_hd_short", + test_ngtcp2_pkt_decode_hd_short) || + !CU_add_test(pSuite, "pkt_decode_stream_frame", + test_ngtcp2_pkt_decode_stream_frame) || + !CU_add_test(pSuite, "pkt_decode_ack_frame", + test_ngtcp2_pkt_decode_ack_frame) || + !CU_add_test(pSuite, "pkt_decode_padding_frame", + test_ngtcp2_pkt_decode_padding_frame) || + !CU_add_test(pSuite, "pkt_encode_stream_frame", + test_ngtcp2_pkt_encode_stream_frame) || + !CU_add_test(pSuite, "pkt_encode_ack_frame", + test_ngtcp2_pkt_encode_ack_frame) || + !CU_add_test(pSuite, "pkt_encode_ack_ecn_frame", + test_ngtcp2_pkt_encode_ack_ecn_frame) || + !CU_add_test(pSuite, "pkt_encode_reset_stream_frame", + test_ngtcp2_pkt_encode_reset_stream_frame) || + !CU_add_test(pSuite, "pkt_encode_connection_close_frame", + test_ngtcp2_pkt_encode_connection_close_frame) || + !CU_add_test(pSuite, "pkt_encode_connection_close_app_frame", + test_ngtcp2_pkt_encode_connection_close_app_frame) || + !CU_add_test(pSuite, "pkt_encode_max_data_frame", + test_ngtcp2_pkt_encode_max_data_frame) || + !CU_add_test(pSuite, "pkt_encode_max_stream_data_frame", + test_ngtcp2_pkt_encode_max_stream_data_frame) || + !CU_add_test(pSuite, "pkt_encode_max_streams_frame", + test_ngtcp2_pkt_encode_max_streams_frame) || + !CU_add_test(pSuite, "pkt_encode_ping_frame", + test_ngtcp2_pkt_encode_ping_frame) || + !CU_add_test(pSuite, "pkt_encode_data_blocked_frame", + test_ngtcp2_pkt_encode_data_blocked_frame) || + !CU_add_test(pSuite, "pkt_encode_stream_data_blocked_frame", + test_ngtcp2_pkt_encode_stream_data_blocked_frame) || + !CU_add_test(pSuite, "pkt_encode_streams_blocked_frame", + test_ngtcp2_pkt_encode_streams_blocked_frame) || + !CU_add_test(pSuite, "pkt_encode_new_connection_id_frame", + test_ngtcp2_pkt_encode_new_connection_id_frame) || + !CU_add_test(pSuite, "pkt_encode_stop_sending_frame", + test_ngtcp2_pkt_encode_stop_sending_frame) || + !CU_add_test(pSuite, "pkt_encode_path_challenge_frame", + test_ngtcp2_pkt_encode_path_challenge_frame) || + !CU_add_test(pSuite, "pkt_encode_path_response_frame", + test_ngtcp2_pkt_encode_path_response_frame) || + !CU_add_test(pSuite, "pkt_encode_crypto_frame", + test_ngtcp2_pkt_encode_crypto_frame) || + !CU_add_test(pSuite, "pkt_encode_new_token_frame", + test_ngtcp2_pkt_encode_new_token_frame) || + !CU_add_test(pSuite, "pkt_encode_retire_connection_id", + test_ngtcp2_pkt_encode_retire_connection_id_frame) || + !CU_add_test(pSuite, "pkt_encode_handshake_done", + test_ngtcp2_pkt_encode_handshake_done_frame) || + !CU_add_test(pSuite, "pkt_encode_datagram_frame", + test_ngtcp2_pkt_encode_datagram_frame) || + !CU_add_test(pSuite, "pkt_adjust_pkt_num", + test_ngtcp2_pkt_adjust_pkt_num) || + !CU_add_test(pSuite, "pkt_validate_ack", test_ngtcp2_pkt_validate_ack) || + !CU_add_test(pSuite, "pkt_write_stateless_reset", + test_ngtcp2_pkt_write_stateless_reset) || + !CU_add_test(pSuite, "pkt_write_retry", test_ngtcp2_pkt_write_retry) || + !CU_add_test(pSuite, "pkt_write_version_negotiation", + test_ngtcp2_pkt_write_version_negotiation) || + !CU_add_test(pSuite, "pkt_stream_max_datalen", + test_ngtcp2_pkt_stream_max_datalen) || + !CU_add_test(pSuite, "get_varint", test_ngtcp2_get_varint) || + !CU_add_test(pSuite, "get_uvarintlen", test_ngtcp2_get_uvarintlen) || + !CU_add_test(pSuite, "put_uvarintlen", test_ngtcp2_put_uvarintlen) || + !CU_add_test(pSuite, "get_uint64", test_ngtcp2_get_uint64) || + !CU_add_test(pSuite, "get_uint48", test_ngtcp2_get_uint48) || + !CU_add_test(pSuite, "get_uint32", test_ngtcp2_get_uint32) || + !CU_add_test(pSuite, "get_uint24", test_ngtcp2_get_uint24) || + !CU_add_test(pSuite, "get_uint16", test_ngtcp2_get_uint16) || + !CU_add_test(pSuite, "get_uint16be", test_ngtcp2_get_uint16be) || + !CU_add_test(pSuite, "nth_server_bidi_id", + test_ngtcp2_nth_server_bidi_id) || + !CU_add_test(pSuite, "nth_server_uni_id", + test_ngtcp2_nth_server_uni_id) || + !CU_add_test(pSuite, "nth_client_bidi_id", + test_ngtcp2_nth_client_bidi_id) || + !CU_add_test(pSuite, "nth_client_uni_id", + test_ngtcp2_nth_client_uni_id) || + !CU_add_test(pSuite, "range_intersect", test_ngtcp2_range_intersect) || + !CU_add_test(pSuite, "range_cut", test_ngtcp2_range_cut) || + !CU_add_test(pSuite, "range_not_after", test_ngtcp2_range_not_after) || + !CU_add_test(pSuite, "ksl_insert", test_ngtcp2_ksl_insert) || + !CU_add_test(pSuite, "ksl_clear", test_ngtcp2_ksl_clear) || + !CU_add_test(pSuite, "ksl_range", test_ngtcp2_ksl_range) || + !CU_add_test(pSuite, "ksl_update_key_range", + test_ngtcp2_ksl_update_key_range) || + !CU_add_test(pSuite, "ksl_dup", test_ngtcp2_ksl_dup) || + !CU_add_test(pSuite, "ksl_remove_hint", test_ngtcp2_ksl_remove_hint) || + !CU_add_test(pSuite, "rob_push", test_ngtcp2_rob_push) || + !CU_add_test(pSuite, "rob_push_random", test_ngtcp2_rob_push_random) || + !CU_add_test(pSuite, "rob_data_at", test_ngtcp2_rob_data_at) || + !CU_add_test(pSuite, "rob_remove_prefix", + test_ngtcp2_rob_remove_prefix) || + !CU_add_test(pSuite, "acktr_add", test_ngtcp2_acktr_add) || + !CU_add_test(pSuite, "acktr_eviction", test_ngtcp2_acktr_eviction) || + !CU_add_test(pSuite, "acktr_forget", test_ngtcp2_acktr_forget) || + !CU_add_test(pSuite, "acktr_recv_ack", test_ngtcp2_acktr_recv_ack) || + !CU_add_test(pSuite, "encode_transport_params", + test_ngtcp2_encode_transport_params) || + !CU_add_test(pSuite, "decode_transport_params_new", + test_ngtcp2_decode_transport_params_new) || + !CU_add_test(pSuite, "rtb_add", test_ngtcp2_rtb_add) || + !CU_add_test(pSuite, "rtb_recv_ack", test_ngtcp2_rtb_recv_ack) || + !CU_add_test(pSuite, "rtb_lost_pkt_ts", test_ngtcp2_rtb_lost_pkt_ts) || + !CU_add_test(pSuite, "rtb_remove_expired_lost_pkt", + test_ngtcp2_rtb_remove_expired_lost_pkt) || + !CU_add_test(pSuite, "rtb_remove_excessive_lost_pkt", + test_ngtcp2_rtb_remove_excessive_lost_pkt) || + !CU_add_test(pSuite, "idtr_open", test_ngtcp2_idtr_open) || + !CU_add_test(pSuite, "ringbuf_push_front", + test_ngtcp2_ringbuf_push_front) || + !CU_add_test(pSuite, "ringbuf_pop_front", + test_ngtcp2_ringbuf_pop_front) || + !CU_add_test(pSuite, "conn_stream_open_close", + test_ngtcp2_conn_stream_open_close) || + !CU_add_test(pSuite, "conn_stream_rx_flow_control", + test_ngtcp2_conn_stream_rx_flow_control) || + !CU_add_test(pSuite, "conn_stream_rx_flow_control_error", + test_ngtcp2_conn_stream_rx_flow_control_error) || + !CU_add_test(pSuite, "conn_stream_tx_flow_control", + test_ngtcp2_conn_stream_tx_flow_control) || + !CU_add_test(pSuite, "conn_rx_flow_control", + test_ngtcp2_conn_rx_flow_control) || + !CU_add_test(pSuite, "conn_rx_flow_control_error", + test_ngtcp2_conn_rx_flow_control_error) || + !CU_add_test(pSuite, "conn_tx_flow_control", + test_ngtcp2_conn_tx_flow_control) || + !CU_add_test(pSuite, "conn_shutdown_stream_write", + test_ngtcp2_conn_shutdown_stream_write) || + !CU_add_test(pSuite, "conn_recv_reset_stream", + test_ngtcp2_conn_recv_reset_stream) || + !CU_add_test(pSuite, "conn_recv_stop_sending", + test_ngtcp2_conn_recv_stop_sending) || + !CU_add_test(pSuite, "conn_recv_conn_id_omitted", + test_ngtcp2_conn_recv_conn_id_omitted) || + !CU_add_test(pSuite, "conn_short_pkt_type", + test_ngtcp2_conn_short_pkt_type) || + !CU_add_test(pSuite, "conn_recv_stateless_reset", + test_ngtcp2_conn_recv_stateless_reset) || + !CU_add_test(pSuite, "conn_recv_retry", test_ngtcp2_conn_recv_retry) || + !CU_add_test(pSuite, "conn_recv_delayed_handshake_pkt", + test_ngtcp2_conn_recv_delayed_handshake_pkt) || + !CU_add_test(pSuite, "conn_recv_max_streams", + test_ngtcp2_conn_recv_max_streams) || + !CU_add_test(pSuite, "conn_handshake", test_ngtcp2_conn_handshake) || + !CU_add_test(pSuite, "conn_handshake_error", + test_ngtcp2_conn_handshake_error) || + !CU_add_test(pSuite, "conn_retransmit_protected", + test_ngtcp2_conn_retransmit_protected) || + !CU_add_test(pSuite, "conn_send_max_stream_data", + test_ngtcp2_conn_send_max_stream_data) || + !CU_add_test(pSuite, "conn_recv_stream_data", + test_ngtcp2_conn_recv_stream_data) || + !CU_add_test(pSuite, "conn_recv_ping", test_ngtcp2_conn_recv_ping) || + !CU_add_test(pSuite, "conn_recv_max_stream_data", + test_ngtcp2_conn_recv_max_stream_data) || + !CU_add_test(pSuite, "conn_send_early_data", + test_ngtcp2_conn_send_early_data) || + !CU_add_test(pSuite, "conn_recv_early_data", + test_ngtcp2_conn_recv_early_data) || + !CU_add_test(pSuite, "conn_recv_compound_pkt", + test_ngtcp2_conn_recv_compound_pkt) || + !CU_add_test(pSuite, "conn_pkt_payloadlen", + test_ngtcp2_conn_pkt_payloadlen) || + !CU_add_test(pSuite, "conn_writev_stream", + test_ngtcp2_conn_writev_stream) || + !CU_add_test(pSuite, "conn_writev_datagram", + test_ngtcp2_conn_writev_datagram) || + !CU_add_test(pSuite, "conn_recv_datagram", + test_ngtcp2_conn_recv_datagram) || + !CU_add_test(pSuite, "conn_recv_new_connection_id", + test_ngtcp2_conn_recv_new_connection_id) || + !CU_add_test(pSuite, "conn_recv_retire_connection_id", + test_ngtcp2_conn_recv_retire_connection_id) || + !CU_add_test(pSuite, "conn_server_path_validation", + test_ngtcp2_conn_server_path_validation) || + !CU_add_test(pSuite, "conn_client_connection_migration", + test_ngtcp2_conn_client_connection_migration) || + !CU_add_test(pSuite, "conn_recv_path_challenge", + test_ngtcp2_conn_recv_path_challenge) || + !CU_add_test(pSuite, "conn_key_update", test_ngtcp2_conn_key_update) || + !CU_add_test(pSuite, "conn_crypto_buffer_exceeded", + test_ngtcp2_conn_crypto_buffer_exceeded) || + !CU_add_test(pSuite, "conn_handshake_probe", + test_ngtcp2_conn_handshake_probe) || + !CU_add_test(pSuite, "conn_handshake_loss", + test_ngtcp2_conn_handshake_loss) || + !CU_add_test(pSuite, "conn_recv_client_initial_retry", + test_ngtcp2_conn_recv_client_initial_retry) || + !CU_add_test(pSuite, "conn_recv_client_initial_token", + test_ngtcp2_conn_recv_client_initial_token) || + !CU_add_test(pSuite, "conn_get_active_dcid", + test_ngtcp2_conn_get_active_dcid) || + !CU_add_test(pSuite, "conn_recv_version_negotiation", + test_ngtcp2_conn_recv_version_negotiation) || + !CU_add_test(pSuite, "conn_send_initial_token", + test_ngtcp2_conn_send_initial_token) || + !CU_add_test(pSuite, "conn_set_remote_transport_params", + test_ngtcp2_conn_set_remote_transport_params) || + !CU_add_test(pSuite, "conn_write_connection_close", + test_ngtcp2_conn_write_connection_close) || + !CU_add_test(pSuite, "conn_write_application_close", + test_ngtcp2_conn_write_application_close) || + !CU_add_test(pSuite, "conn_rtb_reclaim_on_pto", + test_ngtcp2_conn_rtb_reclaim_on_pto) || + !CU_add_test(pSuite, "conn_rtb_reclaim_on_pto_datagram", + test_ngtcp2_conn_rtb_reclaim_on_pto_datagram) || + !CU_add_test(pSuite, "conn_validate_ecn", + test_ngtcp2_conn_validate_ecn) || + !CU_add_test(pSuite, "conn_path_validation", + test_ngtcp2_conn_path_validation) || + !CU_add_test(pSuite, "conn_early_data_sync_stream_data_limit", + test_ngtcp2_conn_early_data_sync_stream_data_limit) || + !CU_add_test(pSuite, "conn_early_data_rejected", + test_ngtcp2_conn_early_data_rejected) || + !CU_add_test(pSuite, "conn_keep_alive", test_ngtcp2_conn_keep_alive) || + !CU_add_test(pSuite, "conn_retire_stale_bound_dcid", + test_ngtcp2_conn_retire_stale_bound_dcid) || + !CU_add_test(pSuite, "conn_get_scid", test_ngtcp2_conn_get_scid) || + !CU_add_test(pSuite, "conn_stream_close", + test_ngtcp2_conn_stream_close) || + !CU_add_test(pSuite, "conn_buffer_pkt", test_ngtcp2_conn_buffer_pkt) || + !CU_add_test(pSuite, "conn_handshake_timeout", + test_ngtcp2_conn_handshake_timeout) || + !CU_add_test(pSuite, "conn_get_connection_close_error", + test_ngtcp2_conn_get_connection_close_error) || + !CU_add_test(pSuite, "conn_version_negotiation", + test_ngtcp2_conn_version_negotiation) || + !CU_add_test(pSuite, "conn_server_negotiate_version", + test_ngtcp2_conn_server_negotiate_version) || + !CU_add_test(pSuite, "conn_pmtud_loss", test_ngtcp2_conn_pmtud_loss) || + !CU_add_test(pSuite, "conn_amplification", + test_ngtcp2_conn_amplification) || + !CU_add_test(pSuite, "conn_new_failmalloc", + test_ngtcp2_conn_new_failmalloc) || + !CU_add_test(pSuite, "accept", test_ngtcp2_accept) || + !CU_add_test(pSuite, "select_version", test_ngtcp2_select_version) || + !CU_add_test(pSuite, "pkt_write_connection_close", + test_ngtcp2_pkt_write_connection_close) || + !CU_add_test(pSuite, "map", test_ngtcp2_map) || + !CU_add_test(pSuite, "map_functional", test_ngtcp2_map_functional) || + !CU_add_test(pSuite, "map_each_free", test_ngtcp2_map_each_free) || + !CU_add_test(pSuite, "map_clear", test_ngtcp2_map_clear) || + !CU_add_test(pSuite, "gaptr_push", test_ngtcp2_gaptr_push) || + !CU_add_test(pSuite, "gaptr_is_pushed", test_ngtcp2_gaptr_is_pushed) || + !CU_add_test(pSuite, "gaptr_drop_first_gap", + test_ngtcp2_gaptr_drop_first_gap) || + !CU_add_test(pSuite, "vec_split", test_ngtcp2_vec_split) || + !CU_add_test(pSuite, "vec_merge", test_ngtcp2_vec_merge) || + !CU_add_test(pSuite, "vec_len_varint", test_ngtcp2_vec_len_varint) || + !CU_add_test(pSuite, "strm_streamfrq_pop", + test_ngtcp2_strm_streamfrq_pop) || + !CU_add_test(pSuite, "strm_streamfrq_unacked_offset", + test_ngtcp2_strm_streamfrq_unacked_offset) || + !CU_add_test(pSuite, "strm_streamfrq_unacked_pop", + test_ngtcp2_strm_streamfrq_unacked_pop) || + !CU_add_test(pSuite, "pv_add_entry", test_ngtcp2_pv_add_entry) || + !CU_add_test(pSuite, "pv_validate", test_ngtcp2_pv_validate) || + !CU_add_test(pSuite, "pmtud_probe", test_ngtcp2_pmtud_probe) || + !CU_add_test(pSuite, "encode_ipv4", test_ngtcp2_encode_ipv4) || + !CU_add_test(pSuite, "encode_ipv6", test_ngtcp2_encode_ipv6) || + !CU_add_test(pSuite, "get_bytes", test_ngtcp2_get_bytes)) { + CU_cleanup_registry(); + return (int)CU_get_error(); + } + + /* Run all tests using the CUnit Basic interface */ + CU_basic_set_mode(CU_BRM_VERBOSE); + CU_basic_run_tests(); + num_tests_failed = CU_get_number_of_tests_failed(); + CU_cleanup_registry(); + if (CU_get_error() == CUE_SUCCESS) { + return (int)num_tests_failed; + } else { + printf("CUnit Error: %s\n", CU_get_error_msg()); + return (int)CU_get_error(); + } +} diff --git a/tests/ngtcp2_acktr_test.c b/tests/ngtcp2_acktr_test.c new file mode 100644 index 0000000..f75336a --- /dev/null +++ b/tests/ngtcp2_acktr_test.c @@ -0,0 +1,367 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_acktr_test.h" + +#include <CUnit/CUnit.h> + +#include "ngtcp2_acktr.h" +#include "ngtcp2_test_helper.h" + +void test_ngtcp2_acktr_add(void) { + const int64_t pkt_nums[] = {1, 5, 7, 6, 2, 3}; + ngtcp2_acktr acktr; + ngtcp2_acktr_entry *ent; + ngtcp2_ksl_it it; + size_t i; + int rv; + const ngtcp2_mem *mem = ngtcp2_mem_default(); + ngtcp2_log log; + + ngtcp2_log_init(&log, NULL, NULL, 0, NULL); + ngtcp2_acktr_init(&acktr, &log, mem); + + for (i = 0; i < ngtcp2_arraylen(pkt_nums); ++i) { + rv = ngtcp2_acktr_add(&acktr, pkt_nums[i], 1, 999); + + CU_ASSERT(0 == rv); + } + + it = ngtcp2_acktr_get(&acktr); + ent = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(7 == ent->pkt_num); + CU_ASSERT(3 == ent->len); + + ngtcp2_ksl_it_next(&it); + ent = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(3 == ent->pkt_num); + CU_ASSERT(3 == ent->len); + + ngtcp2_ksl_it_next(&it); + + CU_ASSERT(ngtcp2_ksl_it_end(&it)); + + ngtcp2_acktr_free(&acktr); + + /* Check all conditions */ + + /* The lower bound returns the one beyond of the last entry. The + added packet number extends the first entry. */ + ngtcp2_acktr_init(&acktr, &log, mem); + + ngtcp2_acktr_add(&acktr, 1, 1, 100); + ngtcp2_acktr_add(&acktr, 0, 1, 101); + + CU_ASSERT(1 == ngtcp2_ksl_len(&acktr.ents)); + + it = ngtcp2_acktr_get(&acktr); + ent = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(1 == ent->pkt_num); + CU_ASSERT(2 == ent->len); + CU_ASSERT(100 == ent->tstamp); + + ngtcp2_acktr_free(&acktr); + + /* The entry is the first one and adding a packet number extends it + to the forward. */ + ngtcp2_acktr_init(&acktr, &log, mem); + + ngtcp2_acktr_add(&acktr, 0, 1, 100); + ngtcp2_acktr_add(&acktr, 1, 1, 101); + + CU_ASSERT(1 == ngtcp2_ksl_len(&acktr.ents)); + + it = ngtcp2_acktr_get(&acktr); + ent = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(1 == ent->pkt_num); + CU_ASSERT(2 == ent->len); + CU_ASSERT(101 == ent->tstamp); + + ngtcp2_acktr_free(&acktr); + + /* The adding entry merges the existing 2 entries. */ + ngtcp2_acktr_init(&acktr, &log, mem); + + ngtcp2_acktr_add(&acktr, 0, 1, 100); + ngtcp2_acktr_add(&acktr, 2, 1, 101); + ngtcp2_acktr_add(&acktr, 3, 1, 102); + + CU_ASSERT(2 == ngtcp2_ksl_len(&acktr.ents)); + + ngtcp2_acktr_add(&acktr, 1, 1, 103); + + CU_ASSERT(1 == ngtcp2_ksl_len(&acktr.ents)); + + it = ngtcp2_acktr_get(&acktr); + ent = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(3 == ent->pkt_num); + CU_ASSERT(4 == ent->len); + CU_ASSERT(102 == ent->tstamp); + + ngtcp2_acktr_free(&acktr); + + /* Adding entry does not merge the existing 2 entries. It extends + the last entry. */ + ngtcp2_acktr_init(&acktr, &log, mem); + + ngtcp2_acktr_add(&acktr, 0, 1, 100); + ngtcp2_acktr_add(&acktr, 3, 1, 101); + ngtcp2_acktr_add(&acktr, 4, 1, 102); + + CU_ASSERT(2 == ngtcp2_ksl_len(&acktr.ents)); + + ngtcp2_acktr_add(&acktr, 1, 1, 103); + + CU_ASSERT(2 == ngtcp2_ksl_len(&acktr.ents)); + + it = ngtcp2_acktr_get(&acktr); + ent = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(4 == ent->pkt_num); + CU_ASSERT(2 == ent->len); + CU_ASSERT(102 == ent->tstamp); + + ngtcp2_ksl_it_next(&it); + ent = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(1 == ent->pkt_num); + CU_ASSERT(2 == ent->len); + CU_ASSERT(103 == ent->tstamp); + + ngtcp2_acktr_free(&acktr); + + /* Adding entry does not merge the existing 2 entries. It extends + the first entry. */ + ngtcp2_acktr_init(&acktr, &log, mem); + + ngtcp2_acktr_add(&acktr, 0, 1, 100); + ngtcp2_acktr_add(&acktr, 3, 1, 101); + ngtcp2_acktr_add(&acktr, 4, 1, 102); + + CU_ASSERT(2 == ngtcp2_ksl_len(&acktr.ents)); + + ngtcp2_acktr_add(&acktr, 2, 1, 103); + + CU_ASSERT(2 == ngtcp2_ksl_len(&acktr.ents)); + + it = ngtcp2_acktr_get(&acktr); + ent = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(4 == ent->pkt_num); + CU_ASSERT(3 == ent->len); + CU_ASSERT(102 == ent->tstamp); + + ngtcp2_ksl_it_next(&it); + ent = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(0 == ent->pkt_num); + CU_ASSERT(1 == ent->len); + CU_ASSERT(100 == ent->tstamp); + + ngtcp2_acktr_free(&acktr); + + /* The added packet number does not extend any entries. */ + ngtcp2_acktr_init(&acktr, &log, mem); + + ngtcp2_acktr_add(&acktr, 0, 1, 0); + ngtcp2_acktr_add(&acktr, 4, 1, 0); + ngtcp2_acktr_add(&acktr, 2, 1, 0); + + CU_ASSERT(3 == ngtcp2_ksl_len(&acktr.ents)); + + ngtcp2_acktr_free(&acktr); +} + +void test_ngtcp2_acktr_eviction(void) { + ngtcp2_acktr acktr; + const ngtcp2_mem *mem = ngtcp2_mem_default(); + size_t i; + ngtcp2_acktr_entry *ent; + const size_t extra = 17; + ngtcp2_log log; + ngtcp2_ksl_it it; + + ngtcp2_log_init(&log, NULL, NULL, 0, NULL); + ngtcp2_acktr_init(&acktr, &log, mem); + + for (i = 0; i < NGTCP2_ACKTR_MAX_ENT + extra; ++i) { + ngtcp2_acktr_add(&acktr, (int64_t)(i * 2), 1, 999 + i); + } + + CU_ASSERT(NGTCP2_ACKTR_MAX_ENT == ngtcp2_ksl_len(&acktr.ents)); + + for (i = 0, it = ngtcp2_acktr_get(&acktr); !ngtcp2_ksl_it_end(&it); + ++i, ngtcp2_ksl_it_next(&it)) { + ent = ngtcp2_ksl_it_get(&it); + + CU_ASSERT((int64_t)((NGTCP2_ACKTR_MAX_ENT + extra - 1) * 2 - i * 2) == + ent->pkt_num); + } + + ngtcp2_acktr_free(&acktr); + + /* Invert insertion order */ + ngtcp2_acktr_init(&acktr, &log, mem); + + for (i = NGTCP2_ACKTR_MAX_ENT + extra; i > 0; --i) { + ngtcp2_acktr_add(&acktr, (int64_t)((i - 1) * 2), 1, 999 + i); + } + + CU_ASSERT(NGTCP2_ACKTR_MAX_ENT == ngtcp2_ksl_len(&acktr.ents)); + + for (i = 0, it = ngtcp2_acktr_get(&acktr); !ngtcp2_ksl_it_end(&it); + ++i, ngtcp2_ksl_it_next(&it)) { + ent = ngtcp2_ksl_it_get(&it); + + CU_ASSERT((int64_t)((NGTCP2_ACKTR_MAX_ENT + extra - 1) * 2 - i * 2) == + ent->pkt_num); + } + + ngtcp2_acktr_free(&acktr); +} + +void test_ngtcp2_acktr_forget(void) { + ngtcp2_acktr acktr; + const ngtcp2_mem *mem = ngtcp2_mem_default(); + size_t i; + ngtcp2_acktr_entry *ent; + ngtcp2_log log; + ngtcp2_ksl_it it; + + ngtcp2_log_init(&log, NULL, NULL, 0, NULL); + ngtcp2_acktr_init(&acktr, &log, mem); + + for (i = 0; i < 7; ++i) { + ngtcp2_acktr_add(&acktr, (int64_t)(i * 2), 1, 999 + i); + } + + CU_ASSERT(7 == ngtcp2_ksl_len(&acktr.ents)); + + it = ngtcp2_acktr_get(&acktr); + ngtcp2_ksl_it_next(&it); + ngtcp2_ksl_it_next(&it); + ngtcp2_ksl_it_next(&it); + ent = ngtcp2_ksl_it_get(&it); + ngtcp2_acktr_forget(&acktr, ent); + + CU_ASSERT(3 == ngtcp2_ksl_len(&acktr.ents)); + + it = ngtcp2_acktr_get(&acktr); + ent = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(12 == ent->pkt_num); + + ngtcp2_ksl_it_next(&it); + ent = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(10 == ent->pkt_num); + + ngtcp2_ksl_it_next(&it); + ent = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(8 == ent->pkt_num); + + it = ngtcp2_acktr_get(&acktr); + ent = ngtcp2_ksl_it_get(&it); + + ngtcp2_acktr_forget(&acktr, ent); + + CU_ASSERT(0 == ngtcp2_ksl_len(&acktr.ents)); + + ngtcp2_acktr_free(&acktr); +} + +void test_ngtcp2_acktr_recv_ack(void) { + ngtcp2_acktr acktr; + const ngtcp2_mem *mem = ngtcp2_mem_default(); + size_t i; + ngtcp2_ack ackfr; + int64_t rpkt_nums[] = { + 4500, 4499, 4497, 4496, 4494, 4493, 4491, 4490, 4488, 4487, 4483, + }; + /* + 4500 4499 + 4497 4496 + 4494 4493 + 4491 4490 + 4488 4487 + 4483 + */ + ngtcp2_acktr_entry *ent; + ngtcp2_log log; + ngtcp2_ksl_it it; + + ngtcp2_log_init(&log, NULL, NULL, 0, NULL); + ngtcp2_acktr_init(&acktr, &log, mem); + + for (i = 0; i < ngtcp2_arraylen(rpkt_nums); ++i) { + ngtcp2_acktr_add(&acktr, rpkt_nums[i], 1, 999 + i); + } + + CU_ASSERT(6 == ngtcp2_ksl_len(&acktr.ents)); + + ngtcp2_acktr_add_ack(&acktr, 998, 4497); + ngtcp2_acktr_add_ack(&acktr, 999, 4499); + + ackfr.type = NGTCP2_FRAME_ACK; + ackfr.largest_ack = 998; + ackfr.ack_delay = 0; + ackfr.first_ack_range = 0; + ackfr.rangecnt = 0; + + ngtcp2_acktr_recv_ack(&acktr, &ackfr); + + CU_ASSERT(1 == ngtcp2_ringbuf_len(&acktr.acks)); + CU_ASSERT(1 == ngtcp2_ksl_len(&acktr.ents)); + + it = ngtcp2_ksl_begin(&acktr.ents); + ent = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(4500 == ent->pkt_num); + CU_ASSERT(2 == ent->len); + + ackfr.type = NGTCP2_FRAME_ACK; + ackfr.largest_ack = 999; + ackfr.ack_delay = 0; + ackfr.first_ack_range = 0; + ackfr.rangecnt = 0; + + ngtcp2_acktr_recv_ack(&acktr, &ackfr); + + CU_ASSERT(0 == ngtcp2_ringbuf_len(&acktr.acks)); + CU_ASSERT(1 == ngtcp2_ksl_len(&acktr.ents)); + + it = ngtcp2_ksl_begin(&acktr.ents); + ent = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(4500 == ent->pkt_num); + CU_ASSERT(1 == ent->len); + + ngtcp2_acktr_free(&acktr); +} diff --git a/tests/ngtcp2_acktr_test.h b/tests/ngtcp2_acktr_test.h new file mode 100644 index 0000000..6e9da5d --- /dev/null +++ b/tests/ngtcp2_acktr_test.h @@ -0,0 +1,37 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_ACKTR_TEST_H +#define NGTCP2_ACKTR_TEST_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +void test_ngtcp2_acktr_add(void); +void test_ngtcp2_acktr_eviction(void); +void test_ngtcp2_acktr_forget(void); +void test_ngtcp2_acktr_recv_ack(void); + +#endif /* NGTCP2_ACKTR_TEST_H */ diff --git a/tests/ngtcp2_conn_test.c b/tests/ngtcp2_conn_test.c new file mode 100644 index 0000000..f982aea --- /dev/null +++ b/tests/ngtcp2_conn_test.c @@ -0,0 +1,9351 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_conn_test.h" + +#include <assert.h> + +#include <CUnit/CUnit.h> + +#include "ngtcp2_conn.h" +#include "ngtcp2_test_helper.h" +#include "ngtcp2_mem.h" +#include "ngtcp2_pkt.h" +#include "ngtcp2_cid.h" +#include "ngtcp2_conv.h" +#include "ngtcp2_vec.h" +#include "ngtcp2_rcvry.h" +#include "ngtcp2_addr.h" +#include "ngtcp2_net.h" + +static void qlog_write(void *user_data, uint32_t flags, const void *data, + size_t datalen) { + (void)user_data; + (void)flags; + (void)data; + (void)datalen; +} + +static int null_encrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *plaintext, size_t plaintextlen, + const uint8_t *nonce, size_t noncelen, + const uint8_t *aad, size_t aadlen) { + (void)dest; + (void)aead; + (void)aead_ctx; + (void)plaintext; + (void)plaintextlen; + (void)nonce; + (void)noncelen; + (void)aad; + (void)aadlen; + + if (plaintextlen && plaintext != dest) { + memcpy(dest, plaintext, plaintextlen); + } + memset(dest + plaintextlen, 0, NGTCP2_FAKE_AEAD_OVERHEAD); + + return 0; +} + +static int null_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *ciphertext, size_t ciphertextlen, + const uint8_t *nonce, size_t noncelen, + const uint8_t *aad, size_t aadlen) { + (void)dest; + (void)aead; + (void)aead_ctx; + (void)ciphertext; + (void)nonce; + (void)noncelen; + (void)aad; + (void)aadlen; + assert(ciphertextlen >= NGTCP2_FAKE_AEAD_OVERHEAD); + memmove(dest, ciphertext, ciphertextlen - NGTCP2_FAKE_AEAD_OVERHEAD); + return 0; +} + +static int fail_decrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *ciphertext, size_t ciphertextlen, + const uint8_t *nonce, size_t noncelen, + const uint8_t *aad, size_t aadlen) { + (void)dest; + (void)aead; + (void)aead_ctx; + (void)ciphertext; + (void)ciphertextlen; + (void)nonce; + (void)noncelen; + (void)aad; + (void)aadlen; + return NGTCP2_ERR_DECRYPT; +} + +static int null_hp_mask(uint8_t *dest, const ngtcp2_crypto_cipher *hp, + const ngtcp2_crypto_cipher_ctx *hp_ctx, + const uint8_t *sample) { + (void)hp; + (void)hp_ctx; + (void)sample; + memcpy(dest, NGTCP2_FAKE_HP_MASK, sizeof(NGTCP2_FAKE_HP_MASK) - 1); + return 0; +} + +static int get_new_connection_id(ngtcp2_conn *conn, ngtcp2_cid *cid, + uint8_t *token, size_t cidlen, + void *user_data) { + (void)user_data; + memset(cid->data, 0, cidlen); + cid->data[0] = (uint8_t)(conn->scid.last_seq + 1); + cid->datalen = cidlen; + memset(token, 0, NGTCP2_STATELESS_RESET_TOKENLEN); + return 0; +} + +static uint8_t null_secret[32]; +static uint8_t null_iv[16]; +static uint8_t null_data[4096]; + +static ngtcp2_crypto_km null_ckm = { + {NULL, 0}, {0}, {null_iv, sizeof(null_iv)}, + -1, 0, NGTCP2_CRYPTO_KM_FLAG_NONE, +}; + +static ngtcp2_path_storage null_path; +static ngtcp2_path_storage new_path; +static ngtcp2_path_storage new_nat_path; + +static ngtcp2_pkt_info null_pi; + +void init_static_path(void) { + path_init(&null_path, 0, 0, 0, 0); + path_init(&new_path, 1, 0, 2, 0); + path_init(&new_nat_path, 0, 0, 0, 1); +} + +static ngtcp2_vec *null_datav(ngtcp2_vec *datav, size_t len) { + datav->base = null_data; + datav->len = len; + return datav; +} + +static void init_crypto_ctx(ngtcp2_crypto_ctx *ctx) { + memset(ctx, 0, sizeof(*ctx)); + ctx->aead.max_overhead = NGTCP2_FAKE_AEAD_OVERHEAD; + ctx->max_encryption = 9999; + ctx->max_decryption_failure = 8888; +} + +static void init_initial_crypto_ctx(ngtcp2_crypto_ctx *ctx) { + memset(ctx, 0, sizeof(*ctx)); + ctx->aead.max_overhead = NGTCP2_INITIAL_AEAD_OVERHEAD; + ctx->max_encryption = 9999; + ctx->max_decryption_failure = 8888; +} + +typedef struct { + uint64_t pkt_num; + /* stream_data is intended to store the arguments passed in + recv_stream_data callback. */ + struct { + int64_t stream_id; + uint32_t flags; + size_t datalen; + } stream_data; + struct { + uint32_t flags; + const uint8_t *data; + size_t datalen; + uint64_t dgram_id; + } datagram; + struct { + uint32_t flags; + int64_t stream_id; + uint64_t app_error_code; + } stream_close; +} my_user_data; + +static int client_initial(ngtcp2_conn *conn, void *user_data) { + (void)user_data; + + ngtcp2_conn_submit_crypto_data(conn, NGTCP2_CRYPTO_LEVEL_INITIAL, null_data, + 217); + + return 0; +} + +static int client_initial_early_data(ngtcp2_conn *conn, void *user_data) { + ngtcp2_crypto_aead_ctx aead_ctx = {0}; + ngtcp2_crypto_cipher_ctx hp_ctx = {0}; + ngtcp2_crypto_ctx crypto_ctx; + + (void)user_data; + + ngtcp2_conn_submit_crypto_data(conn, NGTCP2_CRYPTO_LEVEL_INITIAL, null_data, + 217); + + init_crypto_ctx(&crypto_ctx); + + ngtcp2_conn_set_early_crypto_ctx(conn, &crypto_ctx); + ngtcp2_conn_install_early_key(conn, &aead_ctx, null_iv, sizeof(null_iv), + &hp_ctx); + + return 0; +} + +static int client_initial_large_crypto_early_data(ngtcp2_conn *conn, + void *user_data) { + ngtcp2_crypto_aead_ctx aead_ctx = {0}; + ngtcp2_crypto_cipher_ctx hp_ctx = {0}; + ngtcp2_crypto_ctx crypto_ctx; + + (void)user_data; + + /* Initial CRYPTO data which is larger than a typical single + datagram. */ + ngtcp2_conn_submit_crypto_data(conn, NGTCP2_CRYPTO_LEVEL_INITIAL, null_data, + 1500); + + init_crypto_ctx(&crypto_ctx); + + ngtcp2_conn_set_early_crypto_ctx(conn, &crypto_ctx); + ngtcp2_conn_install_early_key(conn, &aead_ctx, null_iv, sizeof(null_iv), + &hp_ctx); + + return 0; +} + +static int recv_client_initial_no_remote_transport_params( + ngtcp2_conn *conn, const ngtcp2_cid *dcid, void *user_data) { + ngtcp2_crypto_aead_ctx aead_ctx = {0}; + ngtcp2_crypto_cipher_ctx hp_ctx = {0}; + ngtcp2_crypto_ctx ctx; + + (void)dcid; + (void)user_data; + + init_initial_crypto_ctx(&ctx); + + ngtcp2_conn_set_initial_crypto_ctx(conn, &ctx); + ngtcp2_conn_install_initial_key(conn, &aead_ctx, null_iv, &hp_ctx, &aead_ctx, + null_iv, &hp_ctx, sizeof(null_iv)); + + init_crypto_ctx(&ctx); + + ngtcp2_conn_set_crypto_ctx(conn, &ctx); + conn->negotiated_version = conn->client_chosen_version; + ngtcp2_conn_install_rx_handshake_key(conn, &aead_ctx, null_iv, + sizeof(null_iv), &hp_ctx); + ngtcp2_conn_install_tx_handshake_key(conn, &aead_ctx, null_iv, + sizeof(null_iv), &hp_ctx); + + return 0; +} + +static int recv_client_initial(ngtcp2_conn *conn, const ngtcp2_cid *dcid, + void *user_data) { + ngtcp2_transport_params params; + + recv_client_initial_no_remote_transport_params(conn, dcid, user_data); + + ngtcp2_transport_params_default(¶ms); + params.initial_scid = conn->dcid.current.cid; + params.original_dcid = conn->rcid; + ngtcp2_conn_set_remote_transport_params(conn, ¶ms); + + return 0; +} + +static int recv_client_initial_early(ngtcp2_conn *conn, const ngtcp2_cid *dcid, + void *user_data) { + ngtcp2_crypto_aead_ctx aead_ctx = {0}; + ngtcp2_crypto_cipher_ctx hp_ctx = {0}; + ngtcp2_crypto_ctx ctx; + + recv_client_initial(conn, dcid, user_data); + + init_crypto_ctx(&ctx); + + ngtcp2_conn_set_early_crypto_ctx(conn, &ctx); + ngtcp2_conn_install_early_key(conn, &aead_ctx, null_iv, sizeof(null_iv), + &hp_ctx); + + return 0; +} + +static int recv_crypto_data(ngtcp2_conn *conn, ngtcp2_crypto_level crypto_level, + uint64_t offset, const uint8_t *data, + size_t datalen, void *user_data) { + (void)conn; + (void)crypto_level; + (void)offset; + (void)data; + (void)datalen; + (void)user_data; + return 0; +} + +static int recv_crypto_data_server_early_data(ngtcp2_conn *conn, + ngtcp2_crypto_level crypto_level, + uint64_t offset, + const uint8_t *data, + size_t datalen, void *user_data) { + ngtcp2_crypto_aead_ctx aead_ctx = {0}; + ngtcp2_crypto_cipher_ctx hp_ctx = {0}; + + (void)offset; + (void)crypto_level; + (void)data; + (void)datalen; + (void)user_data; + + assert(conn->server); + + ngtcp2_conn_submit_crypto_data(conn, NGTCP2_CRYPTO_LEVEL_INITIAL, null_data, + 179); + + ngtcp2_conn_install_tx_key(conn, null_secret, sizeof(null_secret), &aead_ctx, + null_iv, sizeof(null_iv), &hp_ctx); + + conn->callbacks.recv_crypto_data = recv_crypto_data; + + return 0; +} + +static int update_key(ngtcp2_conn *conn, uint8_t *rx_secret, uint8_t *tx_secret, + ngtcp2_crypto_aead_ctx *rx_aead_ctx, uint8_t *rx_iv, + ngtcp2_crypto_aead_ctx *tx_aead_ctx, uint8_t *tx_iv, + const uint8_t *current_rx_secret, + const uint8_t *current_tx_secret, size_t secretlen, + void *user_data) { + (void)conn; + (void)current_rx_secret; + (void)current_tx_secret; + (void)user_data; + (void)secretlen; + + assert(sizeof(null_secret) == secretlen); + + memset(rx_secret, 0xff, sizeof(null_secret)); + memset(tx_secret, 0xff, sizeof(null_secret)); + rx_aead_ctx->native_handle = NULL; + memset(rx_iv, 0xff, sizeof(null_iv)); + tx_aead_ctx->native_handle = NULL; + memset(tx_iv, 0xff, sizeof(null_iv)); + + return 0; +} + +static int recv_crypto_handshake_error(ngtcp2_conn *conn, + ngtcp2_crypto_level crypto_level, + uint64_t offset, const uint8_t *data, + size_t datalen, void *user_data) { + (void)conn; + (void)crypto_level; + (void)offset; + (void)data; + (void)datalen; + (void)user_data; + return NGTCP2_ERR_CRYPTO; +} + +static int recv_crypto_fatal_alert_generated(ngtcp2_conn *conn, + ngtcp2_crypto_level crypto_level, + uint64_t offset, + const uint8_t *data, + size_t datalen, void *user_data) { + (void)conn; + (void)crypto_level; + (void)offset; + (void)data; + (void)datalen; + (void)user_data; + return NGTCP2_ERR_CRYPTO; +} + +static int recv_crypto_data_server(ngtcp2_conn *conn, + ngtcp2_crypto_level crypto_level, + uint64_t offset, const uint8_t *data, + size_t datalen, void *user_data) { + (void)offset; + (void)data; + (void)datalen; + (void)user_data; + + ngtcp2_conn_submit_crypto_data(conn, + crypto_level == NGTCP2_CRYPTO_LEVEL_INITIAL + ? NGTCP2_CRYPTO_LEVEL_INITIAL + : NGTCP2_CRYPTO_LEVEL_HANDSHAKE, + null_data, 218); + + return 0; +} + +static int recv_stream_data(ngtcp2_conn *conn, uint32_t flags, + int64_t stream_id, uint64_t offset, + const uint8_t *data, size_t datalen, + void *user_data, void *stream_user_data) { + my_user_data *ud = user_data; + (void)conn; + (void)offset; + (void)data; + (void)stream_user_data; + + if (ud) { + ud->stream_data.stream_id = stream_id; + ud->stream_data.flags = flags; + ud->stream_data.datalen = datalen; + } + + return 0; +} + +static int +recv_stream_data_shutdown_stream_read(ngtcp2_conn *conn, uint32_t flags, + int64_t stream_id, uint64_t offset, + const uint8_t *data, size_t datalen, + void *user_data, void *stream_user_data) { + int rv; + + recv_stream_data(conn, flags, stream_id, offset, data, datalen, user_data, + stream_user_data); + + rv = ngtcp2_conn_shutdown_stream_read(conn, stream_id, NGTCP2_APP_ERR01); + if (rv != 0) { + return NGTCP2_ERR_CALLBACK_FAILURE; + } + + return 0; +} + +static int stream_close(ngtcp2_conn *conn, uint32_t flags, int64_t stream_id, + uint64_t app_error_code, void *user_data, + void *stream_user_data) { + my_user_data *ud = user_data; + (void)conn; + (void)stream_user_data; + + if (ud) { + ud->stream_close.flags = flags; + ud->stream_close.stream_id = stream_id; + ud->stream_close.app_error_code = app_error_code; + } + + return 0; +} + +static int recv_retry(ngtcp2_conn *conn, const ngtcp2_pkt_hd *hd, + void *user_data) { + (void)conn; + (void)hd; + (void)user_data; + return 0; +} + +static void genrand(uint8_t *dest, size_t destlen, + const ngtcp2_rand_ctx *rand_ctx) { + (void)rand_ctx; + + memset(dest, 0, destlen); +} + +static int recv_datagram(ngtcp2_conn *conn, uint32_t flags, const uint8_t *data, + size_t datalen, void *user_data) { + my_user_data *ud = user_data; + (void)conn; + (void)flags; + + if (ud) { + ud->datagram.flags = flags; + ud->datagram.data = data; + ud->datagram.datalen = datalen; + } + + return 0; +} + +static int ack_datagram(ngtcp2_conn *conn, uint64_t dgram_id, void *user_data) { + my_user_data *ud = user_data; + (void)conn; + + if (ud) { + ud->datagram.dgram_id = dgram_id; + } + + return 0; +} + +static int get_path_challenge_data(ngtcp2_conn *conn, uint8_t *data, + void *user_data) { + (void)conn; + (void)user_data; + + memset(data, 0, NGTCP2_PATH_CHALLENGE_DATALEN); + + return 0; +} + +static int version_negotiation(ngtcp2_conn *conn, uint32_t version, + const ngtcp2_cid *client_dcid, void *user_data) { + ngtcp2_crypto_aead_ctx aead_ctx = {0}; + ngtcp2_crypto_cipher_ctx hp_ctx = {0}; + (void)client_dcid; + (void)user_data; + + ngtcp2_conn_install_vneg_initial_key(conn, version, &aead_ctx, null_iv, + &hp_ctx, &aead_ctx, null_iv, &hp_ctx, + sizeof(null_iv)); + + return 0; +} + +static void delete_crypto_aead_ctx(ngtcp2_conn *conn, + ngtcp2_crypto_aead_ctx *aead_ctx, + void *user_data) { + (void)conn; + (void)aead_ctx; + (void)user_data; +} + +static void delete_crypto_cipher_ctx(ngtcp2_conn *conn, + ngtcp2_crypto_cipher_ctx *cipher_ctx, + void *user_data) { + (void)conn; + (void)cipher_ctx; + (void)user_data; +} + +static void server_default_settings(ngtcp2_settings *settings) { + memset(settings, 0, sizeof(*settings)); + settings->log_printf = NULL; + settings->initial_ts = 0; + settings->initial_rtt = NGTCP2_DEFAULT_INITIAL_RTT; + settings->max_tx_udp_payload_size = 2048; + settings->no_tx_udp_payload_size_shaping = 1; + settings->handshake_timeout = NGTCP2_DEFAULT_HANDSHAKE_TIMEOUT; +} + +static void server_default_transport_params(ngtcp2_transport_params *params) { + size_t i; + + memset(params, 0, sizeof(*params)); + params->initial_max_stream_data_bidi_local = 65535; + params->initial_max_stream_data_bidi_remote = 65535; + params->initial_max_stream_data_uni = 65535; + params->initial_max_data = 128 * 1024; + params->initial_max_streams_bidi = 3; + params->initial_max_streams_uni = 2; + params->max_idle_timeout = 60 * NGTCP2_SECONDS; + params->max_udp_payload_size = 65535; + params->stateless_reset_token_present = 1; + params->active_connection_id_limit = 8; + for (i = 0; i < NGTCP2_STATELESS_RESET_TOKENLEN; ++i) { + params->stateless_reset_token[i] = (uint8_t)i; + } +} + +static void server_default_callbacks(ngtcp2_callbacks *cb) { + memset(cb, 0, sizeof(*cb)); + cb->recv_client_initial = recv_client_initial; + cb->recv_crypto_data = recv_crypto_data_server; + cb->decrypt = null_decrypt; + cb->encrypt = null_encrypt; + cb->hp_mask = null_hp_mask; + cb->rand = genrand; + cb->get_new_connection_id = get_new_connection_id; + cb->update_key = update_key; + cb->delete_crypto_aead_ctx = delete_crypto_aead_ctx; + cb->delete_crypto_cipher_ctx = delete_crypto_cipher_ctx; + cb->get_path_challenge_data = get_path_challenge_data; + cb->version_negotiation = version_negotiation; +} + +static void server_early_callbacks(ngtcp2_callbacks *cb) { + server_default_callbacks(cb); + + cb->recv_client_initial = recv_client_initial_early; + cb->recv_crypto_data = recv_crypto_data_server_early_data; +} + +static void client_default_settings(ngtcp2_settings *settings) { + memset(settings, 0, sizeof(*settings)); + settings->log_printf = NULL; + settings->initial_ts = 0; + settings->initial_rtt = NGTCP2_DEFAULT_INITIAL_RTT; + settings->max_tx_udp_payload_size = 2048; + settings->no_tx_udp_payload_size_shaping = 1; +} + +static void client_default_transport_params(ngtcp2_transport_params *params) { + memset(params, 0, sizeof(*params)); + params->initial_max_stream_data_bidi_local = 65535; + params->initial_max_stream_data_bidi_remote = 65535; + params->initial_max_stream_data_uni = 65535; + params->initial_max_data = 128 * 1024; + params->initial_max_streams_bidi = 0; + params->initial_max_streams_uni = 2; + params->max_idle_timeout = 60 * NGTCP2_SECONDS; + params->max_udp_payload_size = 65535; + params->stateless_reset_token_present = 0; + params->active_connection_id_limit = 8; +} + +static void client_default_callbacks(ngtcp2_callbacks *cb) { + memset(cb, 0, sizeof(*cb)); + cb->client_initial = client_initial; + cb->recv_crypto_data = recv_crypto_data; + cb->decrypt = null_decrypt; + cb->encrypt = null_encrypt; + cb->hp_mask = null_hp_mask; + cb->recv_retry = recv_retry; + cb->rand = genrand; + cb->get_new_connection_id = get_new_connection_id; + cb->update_key = update_key; + cb->delete_crypto_aead_ctx = delete_crypto_aead_ctx; + cb->delete_crypto_cipher_ctx = delete_crypto_cipher_ctx; + cb->get_path_challenge_data = get_path_challenge_data; + cb->version_negotiation = version_negotiation; +} + +static void client_early_callbacks(ngtcp2_callbacks *cb) { + client_default_callbacks(cb); + + cb->client_initial = client_initial_early_data; +} + +static void conn_set_scid_used(ngtcp2_conn *conn) { + ngtcp2_scid *scid; + ngtcp2_ksl_it it; + int rv; + (void)rv; + + assert(1 + (conn->local.transport_params.preferred_address_present != 0) == + ngtcp2_ksl_len(&conn->scid.set)); + + it = ngtcp2_ksl_begin(&conn->scid.set); + scid = ngtcp2_ksl_it_get(&it); + scid->flags |= NGTCP2_SCID_FLAG_USED; + + assert(NGTCP2_PQ_BAD_INDEX == scid->pe.index); + + rv = ngtcp2_pq_push(&conn->scid.used, &scid->pe); + + assert(0 == rv); +} + +static void +setup_default_server_settings(ngtcp2_conn **pconn, const ngtcp2_path *path, + const ngtcp2_settings *settings, + const ngtcp2_transport_params *params) { + ngtcp2_callbacks cb; + ngtcp2_cid dcid, scid; + ngtcp2_transport_params remote_params; + ngtcp2_crypto_aead_ctx aead_ctx = {0}; + ngtcp2_crypto_cipher_ctx hp_ctx = {0}; + ngtcp2_crypto_ctx crypto_ctx; + + dcid_init(&dcid); + scid_init(&scid); + + init_crypto_ctx(&crypto_ctx); + + server_default_callbacks(&cb); + + ngtcp2_conn_server_new(pconn, &dcid, &scid, path, NGTCP2_PROTO_VER_V1, &cb, + settings, params, + /* mem = */ NULL, NULL); + ngtcp2_conn_set_crypto_ctx(*pconn, &crypto_ctx); + ngtcp2_conn_install_rx_handshake_key(*pconn, &aead_ctx, null_iv, + sizeof(null_iv), &hp_ctx); + ngtcp2_conn_install_tx_handshake_key(*pconn, &aead_ctx, null_iv, + sizeof(null_iv), &hp_ctx); + ngtcp2_conn_install_rx_key(*pconn, null_secret, sizeof(null_secret), + &aead_ctx, null_iv, sizeof(null_iv), &hp_ctx); + ngtcp2_conn_install_tx_key(*pconn, null_secret, sizeof(null_secret), + &aead_ctx, null_iv, sizeof(null_iv), &hp_ctx); + (*pconn)->state = NGTCP2_CS_POST_HANDSHAKE; + (*pconn)->flags |= NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED | + NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED | + NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED | + NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED; + (*pconn)->dcid.current.flags |= NGTCP2_DCID_FLAG_PATH_VALIDATED; + conn_set_scid_used(*pconn); + memset(&remote_params, 0, sizeof(remote_params)); + remote_params.initial_max_stream_data_bidi_local = 64 * 1024; + remote_params.initial_max_stream_data_bidi_remote = 64 * 1024; + remote_params.initial_max_stream_data_uni = 64 * 1024; + remote_params.initial_max_streams_bidi = 0; + remote_params.initial_max_streams_uni = 1; + remote_params.initial_max_data = 64 * 1024; + remote_params.active_connection_id_limit = 8; + remote_params.max_udp_payload_size = NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE; + ngtcp2_transport_params_copy_new(&(*pconn)->remote.transport_params, + &remote_params, (*pconn)->mem); + (*pconn)->local.bidi.max_streams = remote_params.initial_max_streams_bidi; + (*pconn)->local.uni.max_streams = remote_params.initial_max_streams_uni; + (*pconn)->tx.max_offset = remote_params.initial_max_data; + (*pconn)->negotiated_version = (*pconn)->client_chosen_version; +} + +static void setup_default_server(ngtcp2_conn **pconn) { + ngtcp2_settings settings; + ngtcp2_transport_params params; + + server_default_settings(&settings); + server_default_transport_params(¶ms); + + setup_default_server_settings(pconn, &null_path.path, &settings, ¶ms); +} + +static void setup_default_client(ngtcp2_conn **pconn) { + ngtcp2_callbacks cb; + ngtcp2_settings settings; + ngtcp2_transport_params params; + ngtcp2_cid dcid, scid; + ngtcp2_transport_params remote_params; + ngtcp2_crypto_aead_ctx aead_ctx = {0}; + ngtcp2_crypto_cipher_ctx hp_ctx = {0}; + ngtcp2_crypto_ctx crypto_ctx; + + dcid_init(&dcid); + scid_init(&scid); + + init_crypto_ctx(&crypto_ctx); + + client_default_callbacks(&cb); + client_default_settings(&settings); + client_default_transport_params(¶ms); + + ngtcp2_conn_client_new(pconn, &dcid, &scid, &null_path.path, + NGTCP2_PROTO_VER_V1, &cb, &settings, ¶ms, + /* mem = */ NULL, NULL); + ngtcp2_conn_set_crypto_ctx(*pconn, &crypto_ctx); + ngtcp2_conn_install_rx_handshake_key(*pconn, &aead_ctx, null_iv, + sizeof(null_iv), &hp_ctx); + ngtcp2_conn_install_tx_handshake_key(*pconn, &aead_ctx, null_iv, + sizeof(null_iv), &hp_ctx); + ngtcp2_conn_install_rx_key(*pconn, null_secret, sizeof(null_secret), + &aead_ctx, null_iv, sizeof(null_iv), &hp_ctx); + ngtcp2_conn_install_tx_key(*pconn, null_secret, sizeof(null_secret), + &aead_ctx, null_iv, sizeof(null_iv), &hp_ctx); + (*pconn)->state = NGTCP2_CS_POST_HANDSHAKE; + (*pconn)->flags |= NGTCP2_CONN_FLAG_CONN_ID_NEGOTIATED | + NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED | + NGTCP2_CONN_FLAG_HANDSHAKE_COMPLETED_HANDLED | + NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED; + (*pconn)->dcid.current.flags |= NGTCP2_DCID_FLAG_PATH_VALIDATED; + conn_set_scid_used(*pconn); + memset(&remote_params, 0, sizeof(remote_params)); + remote_params.initial_max_stream_data_bidi_local = 64 * 1024; + remote_params.initial_max_stream_data_bidi_remote = 64 * 1024; + remote_params.initial_max_stream_data_uni = 64 * 1024; + remote_params.initial_max_streams_bidi = 1; + remote_params.initial_max_streams_uni = 1; + remote_params.initial_max_data = 64 * 1024; + remote_params.active_connection_id_limit = 8; + remote_params.max_udp_payload_size = NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE; + ngtcp2_transport_params_copy_new(&(*pconn)->remote.transport_params, + &remote_params, (*pconn)->mem); + (*pconn)->local.bidi.max_streams = remote_params.initial_max_streams_bidi; + (*pconn)->local.uni.max_streams = remote_params.initial_max_streams_uni; + (*pconn)->tx.max_offset = remote_params.initial_max_data; + (*pconn)->negotiated_version = (*pconn)->client_chosen_version; + + (*pconn)->dcid.current.flags |= NGTCP2_DCID_FLAG_TOKEN_PRESENT; + memset((*pconn)->dcid.current.token, 0xf1, NGTCP2_STATELESS_RESET_TOKENLEN); +} + +static void setup_handshake_server(ngtcp2_conn **pconn) { + ngtcp2_callbacks cb; + ngtcp2_settings settings; + ngtcp2_transport_params params; + ngtcp2_cid dcid, scid; + uint32_t preferred_versions[] = { + NGTCP2_PROTO_VER_V2_DRAFT, + NGTCP2_PROTO_VER_V1, + }; + + dcid_init(&dcid); + scid_init(&scid); + + server_default_callbacks(&cb); + server_default_settings(&settings); + server_default_transport_params(¶ms); + + settings.preferred_versions = preferred_versions; + settings.preferred_versionslen = ngtcp2_arraylen(preferred_versions); + + ngtcp2_conn_server_new(pconn, &dcid, &scid, &null_path.path, + NGTCP2_PROTO_VER_V1, &cb, &settings, ¶ms, + /* mem = */ NULL, NULL); +} + +static void setup_handshake_client_version(ngtcp2_conn **pconn, + uint32_t client_chosen_version) { + ngtcp2_callbacks cb; + ngtcp2_settings settings; + ngtcp2_transport_params params; + ngtcp2_cid rcid, scid; + ngtcp2_crypto_aead retry_aead = {0, NGTCP2_FAKE_AEAD_OVERHEAD}; + ngtcp2_crypto_aead_ctx aead_ctx = {0}; + ngtcp2_crypto_cipher_ctx hp_ctx = {0}; + ngtcp2_crypto_ctx crypto_ctx; + uint32_t preferred_versions[] = { + NGTCP2_PROTO_VER_V2_DRAFT, + NGTCP2_PROTO_VER_V1, + }; + uint32_t other_versions[] = { + NGTCP2_PROTO_VER_V1, + NGTCP2_PROTO_VER_V2_DRAFT, + }; + + rcid_init(&rcid); + scid_init(&scid); + + init_initial_crypto_ctx(&crypto_ctx); + + client_default_callbacks(&cb); + client_default_settings(&settings); + client_default_transport_params(¶ms); + + settings.preferred_versions = preferred_versions; + settings.preferred_versionslen = ngtcp2_arraylen(preferred_versions); + + settings.other_versions = other_versions; + settings.other_versionslen = ngtcp2_arraylen(other_versions); + + ngtcp2_conn_client_new(pconn, &rcid, &scid, &null_path.path, + client_chosen_version, &cb, &settings, ¶ms, + /* mem = */ NULL, NULL); + ngtcp2_conn_set_initial_crypto_ctx(*pconn, &crypto_ctx); + ngtcp2_conn_install_initial_key(*pconn, &aead_ctx, null_iv, &hp_ctx, + &aead_ctx, null_iv, &hp_ctx, sizeof(null_iv)); + ngtcp2_conn_set_retry_aead(*pconn, &retry_aead, &aead_ctx); +} + +static void setup_handshake_client(ngtcp2_conn **pconn) { + setup_handshake_client_version(pconn, NGTCP2_PROTO_VER_V1); +} + +static void setup_early_server(ngtcp2_conn **pconn) { + ngtcp2_callbacks cb; + ngtcp2_settings settings; + ngtcp2_transport_params params; + ngtcp2_cid dcid, scid; + + dcid_init(&dcid); + scid_init(&scid); + + server_early_callbacks(&cb); + server_default_settings(&settings); + server_default_transport_params(¶ms); + + ngtcp2_conn_server_new(pconn, &dcid, &scid, &null_path.path, + NGTCP2_PROTO_VER_V1, &cb, &settings, ¶ms, + /* mem = */ NULL, NULL); +} + +static void setup_early_client(ngtcp2_conn **pconn) { + ngtcp2_callbacks cb; + ngtcp2_settings settings; + ngtcp2_transport_params params; + ngtcp2_cid rcid, scid; + ngtcp2_crypto_aead_ctx aead_ctx = {0}; + ngtcp2_crypto_cipher_ctx hp_ctx = {0}; + ngtcp2_crypto_ctx crypto_ctx; + + rcid_init(&rcid); + scid_init(&scid); + + init_initial_crypto_ctx(&crypto_ctx); + + client_early_callbacks(&cb); + client_default_settings(&settings); + client_default_transport_params(¶ms); + + ngtcp2_conn_client_new(pconn, &rcid, &scid, &null_path.path, + NGTCP2_PROTO_VER_V1, &cb, &settings, ¶ms, + /* mem = */ NULL, NULL); + ngtcp2_conn_set_initial_crypto_ctx(*pconn, &crypto_ctx); + ngtcp2_conn_install_initial_key(*pconn, &aead_ctx, null_iv, &hp_ctx, + &aead_ctx, null_iv, &hp_ctx, sizeof(null_iv)); + + memset(¶ms, 0, sizeof(params)); + params.initial_max_stream_data_bidi_local = 64 * 1024; + params.initial_max_stream_data_bidi_remote = 64 * 1024; + params.initial_max_stream_data_uni = 64 * 1024; + params.initial_max_streams_bidi = 1; + params.initial_max_streams_uni = 1; + params.initial_max_data = 64 * 1024; + params.active_connection_id_limit = 8; + params.max_udp_payload_size = NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE; + + ngtcp2_conn_set_early_remote_transport_params(*pconn, ¶ms); +} + +void test_ngtcp2_conn_stream_open_close(void) { + ngtcp2_conn *conn; + uint8_t buf[2048]; + size_t pktlen; + ngtcp2_ssize spktlen; + int rv; + ngtcp2_frame fr; + ngtcp2_strm *strm; + int64_t stream_id; + + setup_default_server(&conn); + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.flags = 0; + fr.stream.stream_id = 4; + fr.stream.fin = 0; + fr.stream.offset = 0; + fr.stream.datacnt = 1; + fr.stream.data[0].len = 17; + fr.stream.data[0].base = null_data; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, NULL, buf, pktlen, 1); + + CU_ASSERT(0 == rv); + + strm = ngtcp2_conn_find_stream(conn, 4); + + CU_ASSERT(NGTCP2_STRM_FLAG_NONE == strm->flags); + + fr.stream.fin = 1; + fr.stream.offset = 17; + fr.stream.datacnt = 0; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 2, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, NULL, buf, pktlen, 2); + + CU_ASSERT(0 == rv); + CU_ASSERT(NGTCP2_STRM_FLAG_SHUT_RD == strm->flags); + CU_ASSERT(fr.stream.offset == strm->rx.last_offset); + CU_ASSERT(fr.stream.offset == ngtcp2_strm_rx_offset(strm)); + + spktlen = + ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL, + NGTCP2_WRITE_STREAM_FLAG_FIN, 4, NULL, 0, 3); + + CU_ASSERT(spktlen > 0); + + strm = ngtcp2_conn_find_stream(conn, 4); + + CU_ASSERT(NULL != strm); + + /* Open a remote unidirectional stream */ + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.flags = 0; + fr.stream.stream_id = 2; + fr.stream.fin = 0; + fr.stream.offset = 0; + fr.stream.datacnt = 1; + fr.stream.data[0].len = 19; + fr.stream.data[0].base = null_data; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 3, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 3); + + CU_ASSERT(0 == rv); + + strm = ngtcp2_conn_find_stream(conn, 2); + + CU_ASSERT(NGTCP2_STRM_FLAG_SHUT_WR == strm->flags); + CU_ASSERT(fr.stream.data[0].len == strm->rx.last_offset); + CU_ASSERT(fr.stream.data[0].len == ngtcp2_strm_rx_offset(strm)); + + /* Open a local unidirectional stream */ + rv = ngtcp2_conn_open_uni_stream(conn, &stream_id, NULL); + + CU_ASSERT(0 == rv); + CU_ASSERT(3 == stream_id); + + rv = ngtcp2_conn_open_uni_stream(conn, &stream_id, NULL); + + CU_ASSERT(NGTCP2_ERR_STREAM_ID_BLOCKED == rv); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_stream_rx_flow_control(void) { + ngtcp2_conn *conn; + uint8_t buf[2048]; + size_t pktlen; + ngtcp2_ssize spktlen; + int rv; + ngtcp2_frame fr; + ngtcp2_strm *strm; + size_t i; + int64_t stream_id; + + setup_default_server(&conn); + + conn->local.transport_params.initial_max_stream_data_bidi_remote = 2047; + + for (i = 0; i < 3; ++i) { + stream_id = (int64_t)(i * 4); + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.flags = 0; + fr.stream.stream_id = stream_id; + fr.stream.fin = 0; + fr.stream.offset = 0; + fr.stream.datacnt = 1; + fr.stream.data[0].len = 1024; + fr.stream.data[0].base = null_data; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, (int64_t)i, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1); + + CU_ASSERT(0 == rv); + + strm = ngtcp2_conn_find_stream(conn, stream_id); + + CU_ASSERT(NULL != strm); + + rv = ngtcp2_conn_extend_max_stream_offset(conn, stream_id, + fr.stream.data[0].len); + + CU_ASSERT(0 == rv); + } + + CU_ASSERT(3 == ngtcp2_pq_size(&conn->tx.strmq)); + + strm = ngtcp2_conn_find_stream(conn, 0); + + CU_ASSERT(ngtcp2_strm_is_tx_queued(strm)); + + strm = ngtcp2_conn_find_stream(conn, 4); + + CU_ASSERT(ngtcp2_strm_is_tx_queued(strm)); + + strm = ngtcp2_conn_find_stream(conn, 8); + + CU_ASSERT(ngtcp2_strm_is_tx_queued(strm)); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), 2); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(ngtcp2_pq_empty(&conn->tx.strmq)); + + for (i = 0; i < 3; ++i) { + stream_id = (int64_t)(i * 4); + strm = ngtcp2_conn_find_stream(conn, stream_id); + + CU_ASSERT(2047 + 1024 == strm->rx.max_offset); + } + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_stream_rx_flow_control_error(void) { + ngtcp2_conn *conn; + uint8_t buf[2048]; + size_t pktlen; + int rv; + ngtcp2_frame fr; + + setup_default_server(&conn); + + conn->local.transport_params.initial_max_stream_data_bidi_remote = 1023; + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.flags = 0; + fr.stream.stream_id = 4; + fr.stream.fin = 0; + fr.stream.offset = 0; + fr.stream.datacnt = 1; + fr.stream.data[0].len = 1024; + fr.stream.data[0].base = null_data; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1); + + CU_ASSERT(NGTCP2_ERR_FLOW_CONTROL == rv); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_stream_tx_flow_control(void) { + ngtcp2_conn *conn; + uint8_t buf[2048]; + size_t pktlen; + ngtcp2_ssize spktlen; + int rv; + ngtcp2_frame fr; + ngtcp2_strm *strm; + ngtcp2_ssize nwrite; + int64_t stream_id; + + setup_default_client(&conn); + + conn->remote.transport_params->initial_max_stream_data_bidi_remote = 2047; + + rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + + CU_ASSERT(0 == rv); + + strm = ngtcp2_conn_find_stream(conn, stream_id); + spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), + &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE, + stream_id, null_data, 1024, 1); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(1024 == nwrite); + CU_ASSERT(1024 == strm->tx.offset); + + spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), + &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE, + stream_id, null_data, 1024, 2); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(1023 == nwrite); + CU_ASSERT(2047 == strm->tx.offset); + + spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), + &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE, + stream_id, null_data, 1024, 3); + + CU_ASSERT(NGTCP2_ERR_STREAM_DATA_BLOCKED == spktlen); + + /* We can write 0 length STREAM frame */ + spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), + &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE, + stream_id, null_data, 0, 3); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(0 == nwrite); + CU_ASSERT(2047 == strm->tx.offset); + + fr.type = NGTCP2_FRAME_MAX_STREAM_DATA; + fr.max_stream_data.stream_id = stream_id; + fr.max_stream_data.max_stream_data = 2048; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 4); + + CU_ASSERT(0 == rv); + CU_ASSERT(2048 == strm->tx.max_offset); + + spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), + &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE, + stream_id, null_data, 1024, 5); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(1 == nwrite); + CU_ASSERT(2048 == strm->tx.offset); + + ngtcp2_conn_del(conn); + + /* CWND left is round up to the maximum UDP packet size */ + setup_default_client(&conn); + + conn->cstat.cwnd = 1; + + rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + + CU_ASSERT(0 == rv); + + spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), + &nwrite, NGTCP2_WRITE_STREAM_FLAG_FIN, + stream_id, null_data, 1024, 1); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(1024 == nwrite); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_rx_flow_control(void) { + ngtcp2_conn *conn; + uint8_t buf[2048]; + size_t pktlen; + ngtcp2_ssize spktlen; + int rv; + ngtcp2_frame fr; + + setup_default_server(&conn); + + conn->local.transport_params.initial_max_data = 1024; + conn->rx.window = 1024; + conn->rx.max_offset = 1024; + conn->rx.unsent_max_offset = 1024; + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.flags = 0; + fr.stream.stream_id = 4; + fr.stream.fin = 0; + fr.stream.offset = 0; + fr.stream.datacnt = 1; + fr.stream.data[0].len = 1023; + fr.stream.data[0].base = null_data; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1); + + CU_ASSERT(0 == rv); + + ngtcp2_conn_extend_max_offset(conn, 1023); + + CU_ASSERT(1024 + 1023 == conn->rx.unsent_max_offset); + CU_ASSERT(1024 == conn->rx.max_offset); + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.flags = 0; + fr.stream.stream_id = 4; + fr.stream.fin = 0; + fr.stream.offset = 1023; + fr.stream.datacnt = 1; + fr.stream.data[0].len = 1; + fr.stream.data[0].base = null_data; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 2, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2); + + CU_ASSERT(0 == rv); + + ngtcp2_conn_extend_max_offset(conn, 1); + + CU_ASSERT(2048 == conn->rx.unsent_max_offset); + CU_ASSERT(1024 == conn->rx.max_offset); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), 3); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(2048 == conn->rx.max_offset); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_rx_flow_control_error(void) { + ngtcp2_conn *conn; + uint8_t buf[2048]; + size_t pktlen; + int rv; + ngtcp2_frame fr; + + setup_default_server(&conn); + + conn->local.transport_params.initial_max_data = 1024; + conn->rx.window = 1024; + conn->rx.max_offset = 1024; + conn->rx.unsent_max_offset = 1024; + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.flags = 0; + fr.stream.stream_id = 4; + fr.stream.fin = 0; + fr.stream.offset = 0; + fr.stream.datacnt = 1; + fr.stream.data[0].len = 1025; + fr.stream.data[0].base = null_data; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1); + + CU_ASSERT(NGTCP2_ERR_FLOW_CONTROL == rv); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_tx_flow_control(void) { + ngtcp2_conn *conn; + uint8_t buf[2048]; + size_t pktlen; + ngtcp2_ssize spktlen; + int rv; + ngtcp2_frame fr; + ngtcp2_ssize nwrite; + int64_t stream_id; + + setup_default_client(&conn); + + conn->remote.transport_params->initial_max_data = 2048; + conn->tx.max_offset = 2048; + + rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + + CU_ASSERT(0 == rv); + + spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), + &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE, + stream_id, null_data, 1024, 1); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(1024 == nwrite); + CU_ASSERT(1024 == conn->tx.offset); + + spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), + &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE, + stream_id, null_data, 1023, 2); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(1023 == nwrite); + CU_ASSERT(1024 + 1023 == conn->tx.offset); + + spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), + &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE, + stream_id, null_data, 1024, 3); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(1 == nwrite); + CU_ASSERT(2048 == conn->tx.offset); + + spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), + &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE, + stream_id, null_data, 1024, 4); + + CU_ASSERT(spktlen == 0); + CU_ASSERT(-1 == nwrite); + + fr.type = NGTCP2_FRAME_MAX_DATA; + fr.max_data.max_data = 3072; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 5); + + CU_ASSERT(0 == rv); + CU_ASSERT(3072 == conn->tx.max_offset); + + spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), + &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE, + stream_id, null_data, 1024, 4); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(1024 == nwrite); + CU_ASSERT(3072 == conn->tx.offset); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_shutdown_stream_write(void) { + ngtcp2_conn *conn; + int rv; + ngtcp2_frame_chain *frc; + uint8_t buf[2048]; + ngtcp2_frame fr; + size_t pktlen; + ngtcp2_ssize spktlen; + ngtcp2_strm *strm; + int64_t stream_id; + + /* Stream not found */ + setup_default_server(&conn); + + rv = ngtcp2_conn_shutdown_stream_write(conn, 4, NGTCP2_APP_ERR01); + + CU_ASSERT(0 == rv); + + ngtcp2_conn_del(conn); + + /* Check final_size */ + setup_default_client(&conn); + + ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL, + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, null_data, + 1239, 1); + rv = ngtcp2_conn_shutdown_stream_write(conn, stream_id, NGTCP2_APP_ERR01); + + CU_ASSERT(0 == rv); + + for (frc = conn->pktns.tx.frq; frc; frc = frc->next) { + if (frc->fr.type == NGTCP2_FRAME_RESET_STREAM) { + break; + } + } + + CU_ASSERT(NULL != frc); + CU_ASSERT(stream_id == frc->fr.reset_stream.stream_id); + CU_ASSERT(NGTCP2_APP_ERR01 == frc->fr.reset_stream.app_error_code); + CU_ASSERT(1239 == frc->fr.reset_stream.final_size); + + strm = ngtcp2_conn_find_stream(conn, stream_id); + + CU_ASSERT(NULL != strm); + CU_ASSERT(NGTCP2_APP_ERR01 == strm->app_error_code); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), 2); + + CU_ASSERT(spktlen > 0); + + fr.type = NGTCP2_FRAME_RESET_STREAM; + fr.reset_stream.stream_id = stream_id; + fr.reset_stream.app_error_code = NGTCP2_APP_ERR02; + fr.reset_stream.final_size = 100; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 890, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2); + + CU_ASSERT(0 == rv); + CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, stream_id)); + + fr.type = NGTCP2_FRAME_ACK; + fr.ack.largest_ack = conn->pktns.tx.last_pkt_num; + fr.ack.ack_delay = 0; + fr.ack.first_ack_range = 0; + fr.ack.rangecnt = 0; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 899, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2); + + CU_ASSERT(0 == rv); + CU_ASSERT(NULL == ngtcp2_conn_find_stream(conn, stream_id)); + + ngtcp2_conn_del(conn); + + /* Check that stream is closed when RESET_STREAM is acknowledged */ + setup_default_client(&conn); + + ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.stream_id = stream_id; + fr.stream.fin = 0; + fr.stream.offset = 0; + fr.stream.datacnt = 0; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 119, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1); + + CU_ASSERT(0 == rv); + CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, stream_id)); + + rv = ngtcp2_conn_shutdown_stream_write(conn, stream_id, NGTCP2_APP_ERR01); + + CU_ASSERT(0 == rv); + CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, stream_id)); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), 2); + + CU_ASSERT(spktlen > 0); + + /* Incoming FIN does not close stream */ + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.fin = 1; + fr.stream.offset = 0; + fr.stream.datacnt = 0; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 121, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2); + + CU_ASSERT(0 == rv); + CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, stream_id)); + + fr.type = NGTCP2_FRAME_ACK; + fr.ack.largest_ack = conn->pktns.tx.last_pkt_num; + fr.ack.ack_delay = 0; + fr.ack.first_ack_range = 0; + fr.ack.rangecnt = 0; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 332, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 3); + + CU_ASSERT(0 == rv); + CU_ASSERT(NULL == ngtcp2_conn_find_stream(conn, stream_id)); + + ngtcp2_conn_del(conn); + + /* RESET_STREAM is not sent if all tx data are acknowledged */ + setup_default_client(&conn); + + ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + + spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL, + NGTCP2_WRITE_STREAM_FLAG_FIN, stream_id, + null_data, 0, 3); + + CU_ASSERT(spktlen > 0); + + fr.type = NGTCP2_FRAME_ACK; + fr.ack.largest_ack = conn->pktns.tx.last_pkt_num; + fr.ack.ack_delay = 0; + fr.ack.first_ack_range = 0; + fr.ack.rangecnt = 0; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 999, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 7); + + CU_ASSERT(0 == rv); + + rv = ngtcp2_conn_shutdown_stream_write(conn, stream_id, NGTCP2_APP_ERR01); + + CU_ASSERT(0 == rv); + + strm = ngtcp2_conn_find_stream(conn, stream_id); + + CU_ASSERT(!(strm->flags & NGTCP2_STRM_FLAG_SENT_RST)); + + spktlen = + ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL, + NGTCP2_WRITE_STREAM_FLAG_FIN, -1, NULL, 0, 11); + + CU_ASSERT(0 == spktlen); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_recv_reset_stream(void) { + ngtcp2_conn *conn; + int rv; + uint8_t buf[2048]; + ngtcp2_frame fr; + size_t pktlen; + ngtcp2_ssize spktlen; + ngtcp2_strm *strm; + int64_t stream_id; + + /* Receive RESET_STREAM */ + setup_default_server(&conn); + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.flags = 0; + fr.stream.stream_id = 4; + fr.stream.fin = 0; + fr.stream.offset = 0; + fr.stream.datacnt = 1; + fr.stream.data[0].len = 955; + fr.stream.data[0].base = null_data; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1); + + CU_ASSERT(0 == rv); + + ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL, + NGTCP2_WRITE_STREAM_FLAG_NONE, 4, null_data, 354, 2); + + fr.type = NGTCP2_FRAME_RESET_STREAM; + fr.reset_stream.stream_id = 4; + fr.reset_stream.app_error_code = NGTCP2_APP_ERR02; + fr.reset_stream.final_size = 955; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 2, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 3); + + CU_ASSERT(0 == rv); + + strm = ngtcp2_conn_find_stream(conn, 4); + + CU_ASSERT(strm->flags & NGTCP2_STRM_FLAG_SHUT_RD); + CU_ASSERT(strm->flags & NGTCP2_STRM_FLAG_RECV_RST); + + ngtcp2_conn_del(conn); + + /* Receive RESET_STREAM after sending STOP_SENDING */ + setup_default_server(&conn); + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.flags = 0; + fr.stream.stream_id = 4; + fr.stream.fin = 0; + fr.stream.offset = 0; + fr.stream.datacnt = 1; + fr.stream.data[0].len = 955; + fr.stream.data[0].base = null_data; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1); + + CU_ASSERT(0 == rv); + + ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL, + NGTCP2_WRITE_STREAM_FLAG_NONE, 4, null_data, 354, 2); + ngtcp2_conn_shutdown_stream_read(conn, 4, NGTCP2_APP_ERR01); + ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), 3); + + fr.type = NGTCP2_FRAME_RESET_STREAM; + fr.reset_stream.stream_id = 4; + fr.reset_stream.app_error_code = NGTCP2_APP_ERR02; + fr.reset_stream.final_size = 955; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 2, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 4); + + CU_ASSERT(0 == rv); + CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, 4)); + + ngtcp2_conn_del(conn); + + /* Receive RESET_STREAM after sending RESET_STREAM */ + setup_default_server(&conn); + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.flags = 0; + fr.stream.stream_id = 4; + fr.stream.fin = 0; + fr.stream.offset = 0; + fr.stream.datacnt = 1; + fr.stream.data[0].len = 955; + fr.stream.data[0].base = null_data; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1); + + CU_ASSERT(0 == rv); + + ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL, + NGTCP2_WRITE_STREAM_FLAG_NONE, 4, null_data, 354, 2); + ngtcp2_conn_shutdown_stream_write(conn, 4, NGTCP2_APP_ERR01); + ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), 3); + + fr.type = NGTCP2_FRAME_RESET_STREAM; + fr.reset_stream.stream_id = 4; + fr.reset_stream.app_error_code = NGTCP2_APP_ERR02; + fr.reset_stream.final_size = 955; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 2, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 4); + + CU_ASSERT(0 == rv); + CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, 4)); + + fr.type = NGTCP2_FRAME_ACK; + fr.ack.largest_ack = conn->pktns.tx.last_pkt_num; + fr.ack.ack_delay = 0; + fr.ack.first_ack_range = 0; + fr.ack.rangecnt = 0; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 3, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 5); + + CU_ASSERT(0 == rv); + CU_ASSERT(NULL == ngtcp2_conn_find_stream(conn, 4)); + + ngtcp2_conn_del(conn); + + /* Receive RESET_STREAM after receiving STOP_SENDING */ + setup_default_server(&conn); + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.flags = 0; + fr.stream.stream_id = 4; + fr.stream.fin = 0; + fr.stream.offset = 0; + fr.stream.datacnt = 1; + fr.stream.data[0].len = 955; + fr.stream.data[0].base = null_data; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1); + + CU_ASSERT(0 == rv); + + ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL, + NGTCP2_WRITE_STREAM_FLAG_NONE, 4, null_data, 354, 2); + + fr.type = NGTCP2_FRAME_STOP_SENDING; + fr.stop_sending.stream_id = 4; + fr.stop_sending.app_error_code = NGTCP2_APP_ERR01; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 2, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 3); + + CU_ASSERT(0 == rv); + CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, 4)); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), 4); + + CU_ASSERT(spktlen > 0); + + fr.type = NGTCP2_FRAME_RESET_STREAM; + fr.reset_stream.stream_id = 4; + fr.reset_stream.app_error_code = NGTCP2_APP_ERR02; + fr.reset_stream.final_size = 955; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 3, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 4); + + CU_ASSERT(0 == rv); + CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, 4)); + + fr.type = NGTCP2_FRAME_ACK; + fr.ack.largest_ack = conn->pktns.tx.last_pkt_num; + fr.ack.ack_delay = 0; + fr.ack.first_ack_range = 0; + fr.ack.rangecnt = 0; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 4, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 5); + + CU_ASSERT(0 == rv); + CU_ASSERT(NULL == ngtcp2_conn_find_stream(conn, 4)); + + ngtcp2_conn_del(conn); + + /* final_size in RESET_STREAM exceeds the already received offset */ + setup_default_server(&conn); + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.flags = 0; + fr.stream.stream_id = 4; + fr.stream.fin = 0; + fr.stream.offset = 0; + fr.stream.datacnt = 1; + fr.stream.data[0].len = 955; + fr.stream.data[0].base = null_data; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1); + + CU_ASSERT(0 == rv); + + fr.type = NGTCP2_FRAME_RESET_STREAM; + fr.reset_stream.stream_id = 4; + fr.reset_stream.app_error_code = NGTCP2_APP_ERR02; + fr.reset_stream.final_size = 954; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 2, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2); + + CU_ASSERT(NGTCP2_ERR_FINAL_SIZE == rv); + + ngtcp2_conn_del(conn); + + /* final_size in RESET_STREAM differs from the final offset which + STREAM frame with fin indicated. */ + setup_default_server(&conn); + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.flags = 0; + fr.stream.stream_id = 4; + fr.stream.fin = 1; + fr.stream.offset = 0; + fr.stream.datacnt = 1; + fr.stream.data[0].len = 955; + fr.stream.data[0].base = null_data; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1); + + CU_ASSERT(0 == rv); + + fr.type = NGTCP2_FRAME_RESET_STREAM; + fr.reset_stream.stream_id = 4; + fr.reset_stream.app_error_code = NGTCP2_APP_ERR02; + fr.reset_stream.final_size = 956; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 2, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2); + + CU_ASSERT(NGTCP2_ERR_FINAL_SIZE == rv); + + ngtcp2_conn_del(conn); + + /* RESET_STREAM against local stream which has not been initiated. */ + setup_default_server(&conn); + + fr.type = NGTCP2_FRAME_RESET_STREAM; + fr.reset_stream.stream_id = 1; + fr.reset_stream.app_error_code = NGTCP2_APP_ERR01; + fr.reset_stream.final_size = 0; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1); + + CU_ASSERT(NGTCP2_ERR_STREAM_STATE == rv); + + ngtcp2_conn_del(conn); + + /* RESET_STREAM against remote stream which has not been initiated */ + setup_default_server(&conn); + + fr.type = NGTCP2_FRAME_RESET_STREAM; + fr.reset_stream.stream_id = 0; + fr.reset_stream.app_error_code = NGTCP2_APP_ERR01; + fr.reset_stream.final_size = 1999; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1); + + CU_ASSERT(0 == rv); + CU_ASSERT(NULL == ngtcp2_conn_find_stream(conn, 0)); + CU_ASSERT(4 == conn->remote.bidi.unsent_max_streams); + + ngtcp2_conn_del(conn); + + /* RESET_STREAM against remote stream which is larger than allowed + maximum */ + setup_default_server(&conn); + + fr.type = NGTCP2_FRAME_RESET_STREAM; + fr.reset_stream.stream_id = 16; + fr.reset_stream.app_error_code = NGTCP2_APP_ERR01; + fr.reset_stream.final_size = 0; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1); + + CU_ASSERT(NGTCP2_ERR_STREAM_LIMIT == rv); + + ngtcp2_conn_del(conn); + + /* RESET_STREAM against remote stream which is allowed, and no + ngtcp2_strm object has been created */ + setup_default_server(&conn); + + fr.type = NGTCP2_FRAME_RESET_STREAM; + fr.reset_stream.stream_id = 4; + fr.reset_stream.app_error_code = NGTCP2_APP_ERR01; + fr.reset_stream.final_size = 0; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1); + + CU_ASSERT(0 == rv); + CU_ASSERT( + ngtcp2_idtr_is_open(&conn->remote.bidi.idtr, fr.reset_stream.stream_id)); + + ngtcp2_conn_del(conn); + + /* RESET_STREAM against remote stream which is allowed, and no + ngtcp2_strm object has been created, and final_size violates + connection-level flow control. */ + setup_default_server(&conn); + + conn->local.transport_params.initial_max_stream_data_bidi_remote = 1 << 21; + + fr.type = NGTCP2_FRAME_RESET_STREAM; + fr.reset_stream.stream_id = 4; + fr.reset_stream.app_error_code = NGTCP2_APP_ERR01; + fr.reset_stream.final_size = 1 << 20; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1); + + CU_ASSERT(NGTCP2_ERR_FLOW_CONTROL == rv); + + ngtcp2_conn_del(conn); + + /* RESET_STREAM against remote stream which is allowed, and no + ngtcp2_strm object has been created, and final_size violates + stream-level flow control. */ + setup_default_server(&conn); + + conn->rx.max_offset = 1 << 21; + + fr.type = NGTCP2_FRAME_RESET_STREAM; + fr.reset_stream.stream_id = 4; + fr.reset_stream.app_error_code = NGTCP2_APP_ERR01; + fr.reset_stream.final_size = 1 << 20; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1); + + CU_ASSERT(NGTCP2_ERR_FLOW_CONTROL == rv); + + ngtcp2_conn_del(conn); + + /* final_size in RESET_STREAM violates connection-level flow + control */ + setup_default_server(&conn); + + conn->local.transport_params.initial_max_stream_data_bidi_remote = 1 << 21; + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.flags = 0; + fr.stream.stream_id = 4; + fr.stream.fin = 0; + fr.stream.offset = 0; + fr.stream.datacnt = 1; + fr.stream.data[0].len = 955; + fr.stream.data[0].base = null_data; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1); + + CU_ASSERT(0 == rv); + + fr.type = NGTCP2_FRAME_RESET_STREAM; + fr.reset_stream.stream_id = 4; + fr.reset_stream.app_error_code = NGTCP2_APP_ERR02; + fr.reset_stream.final_size = 1024 * 1024; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 2, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2); + + CU_ASSERT(NGTCP2_ERR_FLOW_CONTROL == rv); + + ngtcp2_conn_del(conn); + + /* final_size in RESET_STREAM violates stream-level flow control */ + setup_default_server(&conn); + + conn->rx.max_offset = 1 << 21; + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.flags = 0; + fr.stream.stream_id = 4; + fr.stream.fin = 0; + fr.stream.offset = 0; + fr.stream.datacnt = 1; + fr.stream.data[0].len = 955; + fr.stream.data[0].base = null_data; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1); + + CU_ASSERT(0 == rv); + + fr.type = NGTCP2_FRAME_RESET_STREAM; + fr.reset_stream.stream_id = 4; + fr.reset_stream.app_error_code = NGTCP2_APP_ERR02; + fr.reset_stream.final_size = 1024 * 1024; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 2, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2); + + CU_ASSERT(NGTCP2_ERR_FLOW_CONTROL == rv); + + ngtcp2_conn_del(conn); + + /* Receiving RESET_STREAM for a local unidirectional stream is a + protocol violation. */ + setup_default_server(&conn); + + rv = ngtcp2_conn_open_uni_stream(conn, &stream_id, NULL); + + CU_ASSERT(0 == rv); + + fr.type = NGTCP2_FRAME_RESET_STREAM; + fr.reset_stream.stream_id = stream_id; + fr.reset_stream.app_error_code = NGTCP2_APP_ERR02; + fr.reset_stream.final_size = 0; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1); + + CU_ASSERT(NGTCP2_ERR_PROTO == rv); + + ngtcp2_conn_del(conn); + + /* RESET_STREAM extends connection window including buffered data */ + setup_default_server(&conn); + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.flags = 0; + fr.stream.stream_id = 4; + fr.stream.fin = 0; + fr.stream.offset = 1; + fr.stream.datacnt = 1; + fr.stream.data[0].len = 955; + fr.stream.data[0].base = null_data; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1); + + CU_ASSERT(0 == rv); + + fr.type = NGTCP2_FRAME_RESET_STREAM; + fr.reset_stream.stream_id = 4; + fr.reset_stream.app_error_code = NGTCP2_APP_ERR02; + fr.reset_stream.final_size = 1024; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 2, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2); + + CU_ASSERT(0 == rv); + CU_ASSERT(1024 == conn->rx.offset); + CU_ASSERT(128 * 1024 + 1024 == conn->rx.unsent_max_offset); + + /* Receiving same RESET_STREAM does not increase rx offsets. */ + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 3); + + CU_ASSERT(0 == rv); + CU_ASSERT(1024 == conn->rx.offset); + CU_ASSERT(128 * 1024 + 1024 == conn->rx.unsent_max_offset); + + ngtcp2_conn_del(conn); + + /* Verify that connection window is properly updated when + RESET_STREAM is received after sending STOP_SENDING */ + setup_default_server(&conn); + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.flags = 0; + fr.stream.stream_id = 4; + fr.stream.fin = 0; + fr.stream.offset = 1; + fr.stream.datacnt = 1; + fr.stream.data[0].len = 955; + fr.stream.data[0].base = null_data; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1); + + CU_ASSERT(0 == rv); + + ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL, + NGTCP2_WRITE_STREAM_FLAG_NONE, 4, null_data, 354, 2); + ngtcp2_conn_shutdown_stream_read(conn, 4, NGTCP2_APP_ERR01); + ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), 3); + + CU_ASSERT(128 * 1024 + 956 == conn->rx.unsent_max_offset); + + fr.type = NGTCP2_FRAME_RESET_STREAM; + fr.reset_stream.stream_id = 4; + fr.reset_stream.app_error_code = NGTCP2_APP_ERR02; + fr.reset_stream.final_size = 957; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 2, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 4); + + CU_ASSERT(0 == rv); + CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, 4)); + CU_ASSERT(128 * 1024 + 956 + 1 == conn->rx.unsent_max_offset); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_recv_stop_sending(void) { + ngtcp2_conn *conn; + int rv; + uint8_t buf[2048]; + ngtcp2_frame fr; + size_t pktlen; + ngtcp2_ssize spktlen; + ngtcp2_strm *strm; + ngtcp2_tstamp t = 0; + int64_t pkt_num = 0; + ngtcp2_frame_chain *frc; + int64_t stream_id; + + /* Receive STOP_SENDING */ + setup_default_client(&conn); + + ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL, + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, null_data, + 333, ++t); + + fr.type = NGTCP2_FRAME_STOP_SENDING; + fr.stop_sending.stream_id = stream_id; + fr.stop_sending.app_error_code = NGTCP2_APP_ERR01; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + strm = ngtcp2_conn_find_stream(conn, stream_id); + + CU_ASSERT(strm->flags & NGTCP2_STRM_FLAG_SHUT_WR); + CU_ASSERT(strm->flags & NGTCP2_STRM_FLAG_SENT_RST); + + for (frc = conn->pktns.tx.frq; frc; frc = frc->next) { + if (frc->fr.type == NGTCP2_FRAME_RESET_STREAM) { + break; + } + } + + CU_ASSERT(NULL != frc); + CU_ASSERT(NGTCP2_APP_ERR01 == frc->fr.reset_stream.app_error_code); + CU_ASSERT(333 == frc->fr.reset_stream.final_size); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + + for (frc = conn->pktns.tx.frq; frc; frc = frc->next) { + if (frc->fr.type == NGTCP2_FRAME_RESET_STREAM) { + break; + } + } + + CU_ASSERT(NULL == frc); + + /* Make sure that receiving duplicated STOP_SENDING does not trigger + another RESET_STREAM. */ + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + strm = ngtcp2_conn_find_stream(conn, stream_id); + + CU_ASSERT(strm->flags & NGTCP2_STRM_FLAG_SHUT_WR); + CU_ASSERT(strm->flags & NGTCP2_STRM_FLAG_SENT_RST); + + for (frc = conn->pktns.tx.frq; frc; frc = frc->next) { + if (frc->fr.type == NGTCP2_FRAME_RESET_STREAM) { + break; + } + } + + CU_ASSERT(NULL == frc); + + ngtcp2_conn_del(conn); + + /* Receive STOP_SENDING after receiving RESET_STREAM */ + setup_default_client(&conn); + + ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL, + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, null_data, + 333, ++t); + + fr.type = NGTCP2_FRAME_RESET_STREAM; + fr.reset_stream.stream_id = stream_id; + fr.reset_stream.app_error_code = NGTCP2_APP_ERR01; + fr.reset_stream.final_size = 0; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + fr.type = NGTCP2_FRAME_STOP_SENDING; + fr.stop_sending.stream_id = stream_id; + fr.stop_sending.app_error_code = NGTCP2_APP_ERR01; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, stream_id)); + + for (frc = conn->pktns.tx.frq; frc; frc = frc->next) { + if (frc->fr.type == NGTCP2_FRAME_RESET_STREAM) { + break; + } + } + + CU_ASSERT(NULL != frc); + CU_ASSERT(NGTCP2_APP_ERR01 == frc->fr.reset_stream.app_error_code); + CU_ASSERT(333 == frc->fr.reset_stream.final_size); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + + fr.type = NGTCP2_FRAME_ACK; + fr.ack.largest_ack = conn->pktns.tx.last_pkt_num; + fr.ack.ack_delay = 0; + fr.ack.first_ack_range = 0; + fr.ack.rangecnt = 0; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(NULL == ngtcp2_conn_find_stream(conn, stream_id)); + + ngtcp2_conn_del(conn); + + /* STOP_SENDING against remote bidirectional stream which has not + been initiated. */ + setup_default_server(&conn); + + fr.type = NGTCP2_FRAME_STOP_SENDING; + fr.stop_sending.stream_id = 0; + fr.stop_sending.app_error_code = NGTCP2_APP_ERR01; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1); + + CU_ASSERT(0 == rv); + + strm = ngtcp2_conn_find_stream(conn, 0); + + CU_ASSERT(NULL != strm); + CU_ASSERT(strm->flags & NGTCP2_STRM_FLAG_SHUT_WR); + + ngtcp2_conn_del(conn); + + /* STOP_SENDING against local bidirectional stream which has not + been initiated. */ + setup_default_server(&conn); + + fr.type = NGTCP2_FRAME_STOP_SENDING; + fr.stop_sending.stream_id = 1; + fr.stop_sending.app_error_code = NGTCP2_APP_ERR01; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1); + + CU_ASSERT(NGTCP2_ERR_STREAM_STATE == rv); + + ngtcp2_conn_del(conn); + + /* Receiving STOP_SENDING for a local unidirectional stream */ + setup_default_server(&conn); + + rv = ngtcp2_conn_open_uni_stream(conn, &stream_id, NULL); + + CU_ASSERT(0 == rv); + + fr.type = NGTCP2_FRAME_STOP_SENDING; + fr.stop_sending.stream_id = stream_id; + fr.stop_sending.app_error_code = NGTCP2_APP_ERR01; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1); + + CU_ASSERT(0 == rv); + CU_ASSERT(NGTCP2_FRAME_RESET_STREAM == conn->pktns.tx.frq->fr.type); + + ngtcp2_conn_del(conn); + + /* STOP_SENDING against local unidirectional stream which has not + been initiated. */ + setup_default_server(&conn); + + fr.type = NGTCP2_FRAME_STOP_SENDING; + fr.stop_sending.stream_id = 3; + fr.stop_sending.app_error_code = NGTCP2_APP_ERR01; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1); + + CU_ASSERT(NGTCP2_ERR_STREAM_STATE == rv); + + ngtcp2_conn_del(conn); + + /* STOP_SENDING against local bidirectional stream in Data Sent + state. Because all data have been acknowledged, and FIN is sent, + RESET_STREAM is not necessary. */ + setup_default_client(&conn); + + rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + + CU_ASSERT(0 == rv); + + spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL, + NGTCP2_WRITE_STREAM_FLAG_FIN, stream_id, + null_data, 1, ++t); + + CU_ASSERT(spktlen > 0); + + fr.type = NGTCP2_FRAME_ACK; + fr.ack.largest_ack = conn->pktns.tx.last_pkt_num; + fr.ack.ack_delay = 0; + fr.ack.first_ack_range = 0; + fr.ack.rangecnt = 0; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 0, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1); + + CU_ASSERT(0 == rv); + + fr.type = NGTCP2_FRAME_STOP_SENDING; + fr.stop_sending.stream_id = stream_id; + fr.stop_sending.app_error_code = NGTCP2_APP_ERR01; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1); + + CU_ASSERT(0 == rv); + CU_ASSERT(NULL == conn->pktns.tx.frq); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_recv_conn_id_omitted(void) { + ngtcp2_conn *conn; + int rv; + uint8_t buf[2048]; + ngtcp2_frame fr; + size_t pktlen; + ngtcp2_ksl_it it; + ngtcp2_scid *scid; + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.flags = 0; + fr.stream.stream_id = 4; + fr.stream.fin = 0; + fr.stream.offset = 0; + fr.stream.datacnt = 1; + fr.stream.data[0].len = 100; + fr.stream.data[0].base = null_data; + + /* Receiving packet which has no connection ID while SCID of server + is not empty. */ + setup_default_server(&conn); + + pktlen = write_pkt(buf, sizeof(buf), /* dcid = */ NULL, 1, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1); + + /* packet is just ignored */ + CU_ASSERT(0 == rv); + CU_ASSERT(NULL == ngtcp2_conn_find_stream(conn, 4)); + + ngtcp2_conn_del(conn); + + /* Allow omission of connection ID */ + setup_default_server(&conn); + ngtcp2_cid_zero(&conn->oscid); + + it = ngtcp2_ksl_begin(&conn->scid.set); + scid = ngtcp2_ksl_it_get(&it); + ngtcp2_cid_zero(&scid->cid); + + pktlen = write_pkt(buf, sizeof(buf), /* dcid = */ NULL, 1, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1); + + CU_ASSERT(0 == rv); + CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, 4)); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_short_pkt_type(void) { + ngtcp2_conn *conn; + ngtcp2_pkt_hd hd; + uint8_t buf[2048]; + ngtcp2_ssize spktlen; + int64_t stream_id; + + /* 1 octet pkt num */ + setup_default_client(&conn); + + ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL, + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + null_data, 19, 1); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(pkt_decode_hd_short_mask(&hd, buf, (size_t)spktlen, + conn->oscid.datalen) > 0); + CU_ASSERT(1 == hd.pkt_numlen); + + ngtcp2_conn_del(conn); + + /* 2 octets pkt num */ + setup_default_client(&conn); + conn->pktns.rtb.largest_acked_tx_pkt_num = 0x6afa2f; + conn->pktns.tx.last_pkt_num = 0x6afd78; + + ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL, + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + null_data, 19, 1); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(pkt_decode_hd_short_mask(&hd, buf, (size_t)spktlen, + conn->oscid.datalen) > 0); + CU_ASSERT(2 == hd.pkt_numlen); + + ngtcp2_conn_del(conn); + + /* 4 octets pkt num */ + setup_default_client(&conn); + conn->pktns.rtb.largest_acked_tx_pkt_num = 0x6afa2f; + conn->pktns.tx.last_pkt_num = 0x6bc106; + + ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL, + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + null_data, 19, 1); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(pkt_decode_hd_short_mask(&hd, buf, (size_t)spktlen, + conn->oscid.datalen) > 0); + CU_ASSERT(3 == hd.pkt_numlen); + + ngtcp2_conn_del(conn); + + /* 1 octet pkt num (largest)*/ + setup_default_client(&conn); + conn->pktns.rtb.largest_acked_tx_pkt_num = 1; + conn->pktns.tx.last_pkt_num = 128; + + ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL, + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + null_data, 19, 1); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(pkt_decode_hd_short_mask(&hd, buf, (size_t)spktlen, + conn->oscid.datalen) > 0); + CU_ASSERT(1 == hd.pkt_numlen); + + ngtcp2_conn_del(conn); + + /* 2 octet pkt num (shortest)*/ + setup_default_client(&conn); + conn->pktns.rtb.largest_acked_tx_pkt_num = 1; + conn->pktns.tx.last_pkt_num = 129; + + ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL, + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + null_data, 19, 1); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(pkt_decode_hd_short_mask(&hd, buf, (size_t)spktlen, + conn->oscid.datalen) > 0); + CU_ASSERT(2 == hd.pkt_numlen); + + ngtcp2_conn_del(conn); + + /* 2 octet pkt num (largest)*/ + setup_default_client(&conn); + conn->pktns.rtb.largest_acked_tx_pkt_num = 1; + conn->pktns.tx.last_pkt_num = 32768; + + ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL, + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + null_data, 19, 1); + + CU_ASSERT(spktlen > 0); + CU_ASSERT( + pkt_decode_hd_short(&hd, buf, (size_t)spktlen, conn->oscid.datalen) > 0); + CU_ASSERT(2 == hd.pkt_numlen); + + ngtcp2_conn_del(conn); + + /* 3 octet pkt num (shortest) */ + setup_default_client(&conn); + conn->pktns.rtb.largest_acked_tx_pkt_num = 1; + conn->pktns.tx.last_pkt_num = 32769; + + ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL, + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + null_data, 19, 1); + + CU_ASSERT(spktlen > 0); + CU_ASSERT( + pkt_decode_hd_short(&hd, buf, (size_t)spktlen, conn->oscid.datalen) > 0); + CU_ASSERT(3 == hd.pkt_numlen); + + ngtcp2_conn_del(conn); + + /* 3 octet pkt num (largest) */ + setup_default_client(&conn); + conn->pktns.rtb.largest_acked_tx_pkt_num = 1; + conn->pktns.tx.last_pkt_num = 8388608; + + ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL, + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + null_data, 19, 1); + + CU_ASSERT(spktlen > 0); + CU_ASSERT( + pkt_decode_hd_short(&hd, buf, (size_t)spktlen, conn->oscid.datalen) > 0); + CU_ASSERT(3 == hd.pkt_numlen); + + ngtcp2_conn_del(conn); + + /* 4 octet pkt num (shortest)*/ + setup_default_client(&conn); + conn->pktns.rtb.largest_acked_tx_pkt_num = 1; + conn->pktns.tx.last_pkt_num = 8388609; + + ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL, + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + null_data, 19, 1); + + CU_ASSERT(spktlen > 0); + CU_ASSERT( + pkt_decode_hd_short(&hd, buf, (size_t)spktlen, conn->oscid.datalen) > 0); + CU_ASSERT(4 == hd.pkt_numlen); + + ngtcp2_conn_del(conn); + + /* Overflow */ + setup_default_client(&conn); + conn->pktns.rtb.largest_acked_tx_pkt_num = 1; + conn->pktns.tx.last_pkt_num = NGTCP2_MAX_PKT_NUM - 1; + + ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL, + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + null_data, 19, 1); + + CU_ASSERT(spktlen > 0); + CU_ASSERT( + pkt_decode_hd_short(&hd, buf, (size_t)spktlen, conn->oscid.datalen) > 0); + CU_ASSERT(4 == hd.pkt_numlen); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_recv_stateless_reset(void) { + ngtcp2_conn *conn; + uint8_t buf[256]; + ngtcp2_ssize spktlen; + int rv; + size_t i; + uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN]; + + for (i = 0; i < NGTCP2_STATELESS_RESET_TOKENLEN; ++i) { + token[i] = (uint8_t)~i; + } + + /* server */ + setup_default_server(&conn); + conn->callbacks.decrypt = fail_decrypt; + conn->pktns.rx.max_pkt_num = 24324325; + + ngtcp2_dcid_set_token(&conn->dcid.current, token); + + spktlen = ngtcp2_pkt_write_stateless_reset( + buf, sizeof(buf), token, null_data, NGTCP2_MIN_STATELESS_RESET_RANDLEN); + + CU_ASSERT(spktlen > 0); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, + (size_t)spktlen, 1); + + CU_ASSERT(NGTCP2_ERR_DRAINING == rv); + CU_ASSERT(NGTCP2_CS_DRAINING == conn->state); + + ngtcp2_conn_del(conn); + + /* client */ + setup_default_client(&conn); + conn->callbacks.decrypt = fail_decrypt; + conn->pktns.rx.max_pkt_num = 3255454; + + ngtcp2_dcid_set_token(&conn->dcid.current, token); + + spktlen = + ngtcp2_pkt_write_stateless_reset(buf, sizeof(buf), token, null_data, 29); + + CU_ASSERT(spktlen > 0); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, + (size_t)spktlen, 1); + + CU_ASSERT(NGTCP2_ERR_DRAINING == rv); + CU_ASSERT(NGTCP2_CS_DRAINING == conn->state); + + ngtcp2_conn_del(conn); + + /* stateless reset in long packet */ + setup_default_server(&conn); + conn->callbacks.decrypt = fail_decrypt; + conn->pktns.rx.max_pkt_num = 754233; + + ngtcp2_dcid_set_token(&conn->dcid.current, token); + + spktlen = ngtcp2_pkt_write_stateless_reset( + buf, sizeof(buf), token, null_data, NGTCP2_MIN_STATELESS_RESET_RANDLEN); + + CU_ASSERT(spktlen > 0); + + /* long packet */ + buf[0] |= NGTCP2_HEADER_FORM_BIT; + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, + (size_t)spktlen, 1); + + CU_ASSERT(NGTCP2_ERR_DRAINING == rv); + CU_ASSERT(NGTCP2_CS_DRAINING == conn->state); + + ngtcp2_conn_del(conn); + + /* stateless reset in long packet; parsing long header fails */ + setup_default_server(&conn); + conn->callbacks.decrypt = fail_decrypt; + conn->pktns.rx.max_pkt_num = 754233; + + ngtcp2_dcid_set_token(&conn->dcid.current, token); + + spktlen = ngtcp2_pkt_write_stateless_reset( + buf, 41, token, null_data, NGTCP2_MIN_STATELESS_RESET_RANDLEN); + + CU_ASSERT(spktlen > 0); + + /* long packet */ + buf[0] |= NGTCP2_HEADER_FORM_BIT; + buf[0] |= 0x30; + /* Make version nonzero so that it does not look like Version + Negotiation packet */ + buf[1] = 0xff; + /* Make largest CID so that ngtcp2_pkt_decode_hd_long fails */ + buf[5] = 0xff; + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, + (size_t)spktlen, 1); + + CU_ASSERT(NGTCP2_ERR_DRAINING == rv); + CU_ASSERT(NGTCP2_CS_DRAINING == conn->state); + + ngtcp2_conn_del(conn); + + /* token does not match */ + setup_default_client(&conn); + conn->callbacks.decrypt = fail_decrypt; + conn->pktns.rx.max_pkt_num = 24324325; + + spktlen = + ngtcp2_pkt_write_stateless_reset(buf, sizeof(buf), token, null_data, 29); + + CU_ASSERT(spktlen > 0); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, + (size_t)spktlen, 1); + + CU_ASSERT(0 == rv); + CU_ASSERT(NGTCP2_CS_DRAINING != conn->state); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_recv_retry(void) { + ngtcp2_conn *conn; + uint8_t buf[2048]; + ngtcp2_ssize spktlen; + uint64_t t = 0; + ngtcp2_cid dcid; + const uint8_t token[] = "address-validation-token"; + size_t i; + int64_t stream_id; + ngtcp2_ssize datalen; + int rv; + ngtcp2_vec datav; + ngtcp2_strm *strm; + ngtcp2_crypto_aead aead = {0}; + ngtcp2_crypto_aead_ctx aead_ctx = {0}; + + dcid_init(&dcid); + setup_handshake_client(&conn); + conn->callbacks.recv_retry = recv_retry; + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + + spktlen = ngtcp2_pkt_write_retry( + buf, sizeof(buf), NGTCP2_PROTO_VER_V1, &conn->oscid, &dcid, + ngtcp2_conn_get_dcid(conn), token, strsize(token), null_encrypt, &aead, + &aead_ctx); + + CU_ASSERT(spktlen > 0); + + for (i = 0; i < 2; ++i) { + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, + (size_t)spktlen, ++t); + + CU_ASSERT(0 == rv); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + if (i == 1) { + /* Retry packet was ignored */ + CU_ASSERT(spktlen == 0); + } else { + CU_ASSERT(spktlen > 0); + CU_ASSERT(1 == conn->in_pktns->tx.last_pkt_num); + CU_ASSERT(ngtcp2_cid_eq(&dcid, ngtcp2_conn_get_dcid(conn))); + CU_ASSERT(conn->flags & NGTCP2_CONN_FLAG_RECV_RETRY); + } + } + + ngtcp2_conn_del(conn); + + /* Retry packet with non-matching tag is rejected */ + setup_handshake_client(&conn); + conn->callbacks.recv_retry = recv_retry; + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + + spktlen = ngtcp2_pkt_write_retry( + buf, sizeof(buf), NGTCP2_PROTO_VER_V1, &conn->oscid, &dcid, + ngtcp2_conn_get_dcid(conn), token, strsize(token), null_encrypt, &aead, + &aead_ctx); + + CU_ASSERT(spktlen > 0); + + /* Change tag */ + buf[spktlen - 1] = 1; + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, + (size_t)spktlen, ++t); + + CU_ASSERT(0 == rv); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(0 == spktlen); + + ngtcp2_conn_del(conn); + + /* Make sure that 0RTT packets are retransmitted */ + setup_early_client(&conn); + conn->callbacks.recv_retry = recv_retry; + + rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + + CU_ASSERT(0 == rv); + + spktlen = + ngtcp2_conn_writev_stream(conn, NULL, NULL, buf, sizeof(buf), &datalen, + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + null_datav(&datav, 219), 1, ++t); + + CU_ASSERT(sizeof(buf) == spktlen); + CU_ASSERT(219 == datalen); + + spktlen = + ngtcp2_conn_writev_stream(conn, NULL, NULL, buf, sizeof(buf), &datalen, + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + null_datav(&datav, 119), 1, ++t); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(119 == datalen); + + spktlen = ngtcp2_pkt_write_retry( + buf, sizeof(buf), NGTCP2_PROTO_VER_V1, &conn->oscid, &dcid, + ngtcp2_conn_get_dcid(conn), token, strsize(token), null_encrypt, &aead, + &aead_ctx); + + CU_ASSERT(spktlen > 0); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, + (size_t)spktlen, ++t); + + CU_ASSERT(0 == rv); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + /* Make sure that resent 0RTT packet is padded */ + CU_ASSERT(sizeof(buf) == spktlen); + CU_ASSERT(2 == conn->pktns.tx.last_pkt_num); + + strm = ngtcp2_conn_find_stream(conn, stream_id); + + CU_ASSERT(0 == ngtcp2_ksl_len(strm->tx.streamfrq)); + + /* ngtcp2_conn_write_stream sends new 0RTT packet. */ + spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), + &datalen, NGTCP2_WRITE_STREAM_FLAG_NONE, + stream_id, null_data, 120, ++t); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(3 == conn->pktns.tx.last_pkt_num); + CU_ASSERT(120 == datalen); + CU_ASSERT(NULL == conn->pktns.tx.frq); + CU_ASSERT(!ngtcp2_rtb_empty(&conn->pktns.rtb)); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_recv_delayed_handshake_pkt(void) { + ngtcp2_conn *conn; + uint8_t buf[2048]; + size_t pktlen; + ngtcp2_frame fr; + int rv; + + setup_default_client(&conn); + + fr.type = NGTCP2_FRAME_CRYPTO; + fr.crypto.offset = 0; + fr.crypto.datacnt = 1; + fr.crypto.data[0].len = 567; + fr.crypto.data[0].base = null_data; + + pktlen = write_handshake_pkt(buf, sizeof(buf), &conn->oscid, + ngtcp2_conn_get_dcid(conn), 1, + NGTCP2_PROTO_VER_V1, &fr, 1, &null_ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1); + + CU_ASSERT(0 == rv); + CU_ASSERT(1 == ngtcp2_ksl_len(&conn->hs_pktns->acktr.ents)); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_recv_max_streams(void) { + ngtcp2_conn *conn; + uint8_t buf[2048]; + size_t pktlen; + int rv; + ngtcp2_frame fr; + + setup_default_client(&conn); + + fr.type = NGTCP2_FRAME_MAX_STREAMS_UNI; + fr.max_streams.max_streams = 999; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 1); + + CU_ASSERT(0 == rv); + CU_ASSERT(999 == conn->local.uni.max_streams); + + fr.type = NGTCP2_FRAME_MAX_STREAMS_BIDI; + fr.max_streams.max_streams = 997; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 2, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2); + + CU_ASSERT(0 == rv); + CU_ASSERT(997 == conn->local.bidi.max_streams); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_handshake(void) { + ngtcp2_conn *conn; + uint8_t buf[2048]; + size_t pktlen; + ngtcp2_ssize spktlen; + ngtcp2_frame fr; + int64_t pkt_num = 12345689; + ngtcp2_tstamp t = 0; + ngtcp2_cid rcid; + int rv; + int64_t stream_id; + ngtcp2_ssize nwrite; + ngtcp2_crypto_aead_ctx aead_ctx = {0}; + ngtcp2_crypto_cipher_ctx hp_ctx = {0}; + ngtcp2_crypto_ctx crypto_ctx; + + rcid_init(&rcid); + + /* Make sure server Initial is padded */ + setup_handshake_server(&conn); + fr.type = NGTCP2_FRAME_CRYPTO; + fr.crypto.offset = 0; + fr.crypto.datacnt = 1; + fr.crypto.data[0].len = 1200; + fr.crypto.data[0].base = null_data; + + pktlen = write_initial_pkt( + buf, sizeof(buf), &rcid, ngtcp2_conn_get_dcid(conn), ++pkt_num, + conn->client_chosen_version, NULL, 0, &fr, 1, &null_ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen >= 1200); + + ngtcp2_conn_del(conn); + + /* Make sure server Handshake is padded when ack-eliciting Initial + is coalesced. */ + setup_handshake_server(&conn); + fr.type = NGTCP2_FRAME_CRYPTO; + fr.crypto.offset = 0; + fr.crypto.datacnt = 1; + fr.crypto.data[0].len = 1200; + fr.crypto.data[0].base = null_data; + + pktlen = write_initial_pkt( + buf, sizeof(buf), &rcid, ngtcp2_conn_get_dcid(conn), ++pkt_num, + conn->client_chosen_version, NULL, 0, &fr, 1, &null_ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + ngtcp2_conn_submit_crypto_data(conn, NGTCP2_CRYPTO_LEVEL_HANDSHAKE, null_data, + 91); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen >= 1200); + CU_ASSERT(1 == ngtcp2_ksl_len(&conn->hs_pktns->rtb.ents)); + + ngtcp2_conn_del(conn); + + /* Make sure that client packet is padded if it includes Initial and + 0RTT packets */ + setup_early_client(&conn); + + conn->callbacks.client_initial = client_initial_large_crypto_early_data; + + rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + + CU_ASSERT(0 == rv); + + /* First packet should only includes Initial. No space for 0RTT. */ + spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, 1280, &nwrite, + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + null_data, 10, ++t); + + CU_ASSERT(1280 == spktlen); + CU_ASSERT(-1 == nwrite); + + /* Second packet has a room for 0RTT. */ + spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, 1280, &nwrite, + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + null_data, 10, ++t); + + CU_ASSERT(1280 == spktlen); + CU_ASSERT(10 == nwrite); + + /* We have no data to send. */ + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, 1280, ++t); + + CU_ASSERT(0 == spktlen); + + ngtcp2_conn_del(conn); + + /* Make sure that client non ack-eliciting Initial triggers + padding. */ + setup_handshake_client(&conn); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen >= 1200); + + fr.type = NGTCP2_FRAME_CRYPTO; + fr.crypto.offset = 0; + fr.crypto.datacnt = 1; + fr.crypto.data[0].len = 1200; + fr.crypto.data[0].base = null_data; + + pktlen = write_initial_pkt( + buf, sizeof(buf), &conn->oscid, ngtcp2_conn_get_dcid(conn), ++pkt_num, + conn->client_chosen_version, NULL, 0, &fr, 1, &null_ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + init_crypto_ctx(&crypto_ctx); + ngtcp2_conn_set_crypto_ctx(conn, &crypto_ctx); + ngtcp2_conn_install_rx_handshake_key(conn, &aead_ctx, null_iv, + sizeof(null_iv), &hp_ctx); + ngtcp2_conn_install_tx_handshake_key(conn, &aead_ctx, null_iv, + sizeof(null_iv), &hp_ctx); + + pktlen = write_handshake_pkt(buf, sizeof(buf), &conn->oscid, + ngtcp2_conn_get_dcid(conn), ++pkt_num, + conn->client_chosen_version, &fr, 1, &null_ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen >= 1200); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_handshake_error(void) { + ngtcp2_conn *conn; + uint8_t buf[2048]; + size_t pktlen; + ngtcp2_ssize spktlen; + ngtcp2_frame fr; + int64_t pkt_num = 107; + ngtcp2_tstamp t = 0; + ngtcp2_cid rcid; + int rv; + + rcid_init(&rcid); + + /* client side */ + setup_handshake_client(&conn); + conn->callbacks.recv_crypto_data = recv_crypto_handshake_error; + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + + fr.type = NGTCP2_FRAME_CRYPTO; + fr.crypto.offset = 0; + fr.crypto.datacnt = 1; + fr.crypto.data[0].len = 333; + fr.crypto.data[0].base = null_data; + + pktlen = write_initial_pkt( + buf, sizeof(buf), &conn->oscid, ngtcp2_conn_get_dcid(conn), ++pkt_num, + conn->client_chosen_version, NULL, 0, &fr, 1, &null_ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(NGTCP2_ERR_CRYPTO == rv); + + ngtcp2_conn_del(conn); + + /* server side */ + setup_handshake_server(&conn); + conn->callbacks.recv_crypto_data = recv_crypto_handshake_error; + + fr.type = NGTCP2_FRAME_CRYPTO; + fr.crypto.offset = 0; + fr.crypto.datacnt = 1; + fr.crypto.data[0].len = 1200; + fr.crypto.data[0].base = null_data; + + pktlen = write_initial_pkt( + buf, sizeof(buf), &rcid, ngtcp2_conn_get_dcid(conn), ++pkt_num, + conn->client_chosen_version, NULL, 0, &fr, 1, &null_ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(NGTCP2_ERR_CRYPTO == rv); + + ngtcp2_conn_del(conn); + + /* server side; wrong version */ + setup_handshake_server(&conn); + conn->callbacks.recv_crypto_data = recv_crypto_handshake_error; + + fr.type = NGTCP2_FRAME_CRYPTO; + fr.crypto.offset = 0; + fr.crypto.datacnt = 1; + fr.crypto.data[0].len = 1201; + fr.crypto.data[0].base = null_data; + + pktlen = + write_initial_pkt(buf, sizeof(buf), &rcid, ngtcp2_conn_get_dcid(conn), + ++pkt_num, 0xffff, NULL, 0, &fr, 1, &null_ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(NGTCP2_ERR_DROP_CONN == rv); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_retransmit_protected(void) { + ngtcp2_conn *conn; + uint8_t buf[2048]; + ngtcp2_ssize spktlen; + ngtcp2_tstamp t = 0; + int64_t stream_id, stream_id_a, stream_id_b; + ngtcp2_ksl_it it; + ngtcp2_frame fr; + size_t pktlen; + ngtcp2_vec datav; + int accepted; + int rv; + ngtcp2_strm *strm; + + /* Retransmit a packet completely */ + setup_default_client(&conn); + + ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL, + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + null_data, 126, ++t); + + CU_ASSERT(spktlen > 0); + + /* Kick delayed ACK timer */ + t += NGTCP2_SECONDS; + + conn->pktns.tx.last_pkt_num = 1000000009; + conn->pktns.rtb.largest_acked_tx_pkt_num = 1000000007; + it = ngtcp2_rtb_head(&conn->pktns.rtb); + ngtcp2_conn_detect_lost_pkt(conn, &conn->pktns, &conn->cstat, ++t); + + strm = ngtcp2_conn_find_stream(conn, stream_id); + + CU_ASSERT(1 == strm->tx.loss_count); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(NULL == conn->pktns.tx.frq); + + it = ngtcp2_rtb_head(&conn->pktns.rtb); + + CU_ASSERT(!ngtcp2_ksl_it_end(&it)); + + ngtcp2_conn_del(conn); + + /* Retransmission takes place per frame basis. */ + setup_default_client(&conn); + conn->local.bidi.max_streams = 3; + + ngtcp2_conn_open_bidi_stream(conn, &stream_id_a, NULL); + ngtcp2_conn_open_bidi_stream(conn, &stream_id_b, NULL); + + ngtcp2_conn_shutdown_stream_write(conn, stream_id_a, NGTCP2_APP_ERR01); + ngtcp2_conn_shutdown_stream_write(conn, stream_id_b, NGTCP2_APP_ERR01); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + + /* Kick delayed ACK timer */ + t += NGTCP2_SECONDS; + + conn->pktns.tx.last_pkt_num = 1000000009; + conn->pktns.rtb.largest_acked_tx_pkt_num = 1000000007; + it = ngtcp2_rtb_head(&conn->pktns.rtb); + ngtcp2_conn_detect_lost_pkt(conn, &conn->pktns, &conn->cstat, ++t); + spktlen = + ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, (size_t)(spktlen - 1), ++t); + + CU_ASSERT(spktlen > 0); + + it = ngtcp2_rtb_head(&conn->pktns.rtb); + + CU_ASSERT(!ngtcp2_ksl_it_end(&it)); + CU_ASSERT(NULL != conn->pktns.tx.frq); + + ngtcp2_conn_del(conn); + + /* DATAGRAM frame must not be retransmitted */ + setup_default_client(&conn); + + conn->callbacks.ack_datagram = ack_datagram; + conn->remote.transport_params->max_datagram_frame_size = 65535; + + ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + + fr.type = NGTCP2_FRAME_ACK; + fr.ack.largest_ack = conn->pktns.tx.last_pkt_num; + fr.ack.ack_delay = 0; + fr.ack.first_ack_range = 0; + fr.ack.rangecnt = 0; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 0, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + datav.base = null_data; + datav.len = 99; + + spktlen = ngtcp2_conn_writev_datagram( + conn, NULL, NULL, buf, sizeof(buf), &accepted, + NGTCP2_WRITE_DATAGRAM_FLAG_NONE, 1000000009, &datav, 1, ++t); + + CU_ASSERT(spktlen > 0); + + /* Kick delayed ACK timer */ + t += NGTCP2_SECONDS; + + conn->pktns.tx.last_pkt_num = 1000000009; + conn->pktns.rtb.largest_acked_tx_pkt_num = 1000000007; + it = ngtcp2_rtb_head(&conn->pktns.rtb); + ngtcp2_conn_detect_lost_pkt(conn, &conn->pktns, &conn->cstat, ++t); + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(0 == spktlen); + CU_ASSERT(NULL == conn->pktns.tx.frq); + + it = ngtcp2_rtb_head(&conn->pktns.rtb); + + CU_ASSERT(!ngtcp2_ksl_it_end(&it)); + + ngtcp2_conn_del(conn); + + /* Retransmit an empty STREAM frame */ + setup_default_client(&conn); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + + fr.type = NGTCP2_FRAME_ACK; + fr.ack.largest_ack = conn->pktns.tx.last_pkt_num; + fr.ack.ack_delay = 0; + fr.ack.first_ack_range = 0; + fr.ack.rangecnt = 0; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 0, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL, + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + NULL, 0, ++t); + + CU_ASSERT(spktlen > 0); + + /* Kick delayed ACK timer */ + t += NGTCP2_SECONDS; + + conn->pktns.tx.last_pkt_num = 1000000009; + conn->pktns.rtb.largest_acked_tx_pkt_num = 1000000007; + it = ngtcp2_rtb_head(&conn->pktns.rtb); + ngtcp2_conn_detect_lost_pkt(conn, &conn->pktns, &conn->cstat, ++t); + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(NULL == conn->pktns.tx.frq); + + it = ngtcp2_rtb_head(&conn->pktns.rtb); + + CU_ASSERT(!ngtcp2_ksl_it_end(&it)); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_send_max_stream_data(void) { + ngtcp2_conn *conn; + uint8_t buf[2048]; + size_t pktlen; + ngtcp2_strm *strm; + int64_t pkt_num = 890; + ngtcp2_tstamp t = 0; + ngtcp2_frame fr; + int rv; + const uint32_t datalen = 1024; + + /* MAX_STREAM_DATA should be sent */ + setup_default_server(&conn); + conn->local.transport_params.initial_max_stream_data_bidi_remote = datalen; + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.stream_id = 4; + fr.stream.fin = 0; + fr.stream.offset = 0; + fr.stream.datacnt = 1; + fr.stream.data[0].len = datalen; + fr.stream.data[0].base = null_data; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + rv = ngtcp2_conn_extend_max_stream_offset(conn, 4, datalen); + + CU_ASSERT(0 == rv); + + strm = ngtcp2_conn_find_stream(conn, 4); + + CU_ASSERT(ngtcp2_strm_is_tx_queued(strm)); + + ngtcp2_conn_del(conn); + + /* MAX_STREAM_DATA should not be sent on incoming fin */ + setup_default_server(&conn); + conn->local.transport_params.initial_max_stream_data_bidi_remote = datalen; + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.stream_id = 4; + fr.stream.fin = 1; + fr.stream.offset = 0; + fr.stream.datacnt = 1; + fr.stream.data[0].len = datalen; + fr.stream.data[0].base = null_data; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + rv = ngtcp2_conn_extend_max_stream_offset(conn, 4, datalen); + + CU_ASSERT(0 == rv); + + strm = ngtcp2_conn_find_stream(conn, 4); + + CU_ASSERT(!ngtcp2_strm_is_tx_queued(strm)); + + ngtcp2_conn_del(conn); + + /* MAX_STREAM_DATA should not be sent if STOP_SENDING frame is being + sent by local endpoint */ + setup_default_server(&conn); + conn->local.transport_params.initial_max_stream_data_bidi_remote = datalen; + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.stream_id = 4; + fr.stream.fin = 0; + fr.stream.offset = 0; + fr.stream.datacnt = 1; + fr.stream.data[0].len = datalen; + fr.stream.data[0].base = null_data; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + rv = ngtcp2_conn_shutdown_stream_read(conn, 4, NGTCP2_APP_ERR01); + + CU_ASSERT(0 == rv); + + rv = ngtcp2_conn_extend_max_stream_offset(conn, 4, datalen); + + CU_ASSERT(0 == rv); + + strm = ngtcp2_conn_find_stream(conn, 4); + + CU_ASSERT(!ngtcp2_strm_is_tx_queued(strm)); + + ngtcp2_conn_del(conn); + + /* MAX_STREAM_DATA should not be sent if stream is being reset by + remote endpoint */ + setup_default_server(&conn); + conn->local.transport_params.initial_max_stream_data_bidi_remote = datalen; + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.stream_id = 4; + fr.stream.fin = 0; + fr.stream.offset = 0; + fr.stream.datacnt = 1; + fr.stream.data[0].len = datalen; + fr.stream.data[0].base = null_data; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + fr.type = NGTCP2_FRAME_RESET_STREAM; + fr.reset_stream.stream_id = 4; + fr.reset_stream.app_error_code = NGTCP2_APP_ERR01; + fr.reset_stream.final_size = datalen; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + rv = ngtcp2_conn_extend_max_stream_offset(conn, 4, datalen); + + CU_ASSERT(0 == rv); + CU_ASSERT(ngtcp2_pq_empty(&conn->tx.strmq)); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_recv_stream_data(void) { + uint8_t buf[1024]; + ngtcp2_conn *conn; + my_user_data ud; + int64_t pkt_num = 612; + ngtcp2_tstamp t = 0; + ngtcp2_frame fr; + size_t pktlen; + int rv; + int64_t stream_id; + size_t i; + + /* 2 STREAM frames are received in the correct order. */ + setup_default_server(&conn); + conn->callbacks.recv_stream_data = recv_stream_data; + conn->user_data = &ud; + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.stream_id = 4; + fr.stream.fin = 0; + fr.stream.offset = 0; + fr.stream.datacnt = 1; + fr.stream.data[0].len = 111; + fr.stream.data[0].base = null_data; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + memset(&ud, 0, sizeof(ud)); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(4 == ud.stream_data.stream_id); + CU_ASSERT(!(ud.stream_data.flags & NGTCP2_STREAM_DATA_FLAG_FIN)); + CU_ASSERT(!(ud.stream_data.flags & NGTCP2_STREAM_DATA_FLAG_EARLY)); + CU_ASSERT(111 == ud.stream_data.datalen); + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.stream_id = 4; + fr.stream.fin = 1; + fr.stream.offset = 111; + fr.stream.datacnt = 1; + fr.stream.data[0].len = 99; + fr.stream.data[0].base = null_data; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + memset(&ud, 0, sizeof(ud)); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(4 == ud.stream_data.stream_id); + CU_ASSERT(ud.stream_data.flags & NGTCP2_STREAM_DATA_FLAG_FIN); + CU_ASSERT(99 == ud.stream_data.datalen); + + ngtcp2_conn_del(conn); + + /* 2 STREAM frames are received in the correct order, and 2nd STREAM + frame has 0 length, and FIN bit set. */ + setup_default_server(&conn); + conn->callbacks.recv_stream_data = recv_stream_data; + conn->user_data = &ud; + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.stream_id = 4; + fr.stream.fin = 0; + fr.stream.offset = 0; + fr.stream.datacnt = 1; + fr.stream.data[0].len = 111; + fr.stream.data[0].base = null_data; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + memset(&ud, 0, sizeof(ud)); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(4 == ud.stream_data.stream_id); + CU_ASSERT(!(ud.stream_data.flags & NGTCP2_STREAM_DATA_FLAG_FIN)); + CU_ASSERT(111 == ud.stream_data.datalen); + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.stream_id = 4; + fr.stream.fin = 1; + fr.stream.offset = 111; + fr.stream.datacnt = 0; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + memset(&ud, 0, sizeof(ud)); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(4 == ud.stream_data.stream_id); + CU_ASSERT(ud.stream_data.flags & NGTCP2_STREAM_DATA_FLAG_FIN); + CU_ASSERT(0 == ud.stream_data.datalen); + + ngtcp2_conn_del(conn); + + /* 2 identical STREAM frames with FIN bit set are received. The + recv_stream_data callback should not be called for sencond STREAM + frame. */ + setup_default_server(&conn); + conn->callbacks.recv_stream_data = recv_stream_data; + conn->user_data = &ud; + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.stream_id = 4; + fr.stream.fin = 1; + fr.stream.offset = 0; + fr.stream.datacnt = 1; + fr.stream.data[0].len = 111; + fr.stream.data[0].base = null_data; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + memset(&ud, 0, sizeof(ud)); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(4 == ud.stream_data.stream_id); + CU_ASSERT(ud.stream_data.flags & NGTCP2_STREAM_DATA_FLAG_FIN); + CU_ASSERT(111 == ud.stream_data.datalen); + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + memset(&ud, 0, sizeof(ud)); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(0 == ud.stream_data.stream_id); + CU_ASSERT(!(ud.stream_data.flags & NGTCP2_STREAM_DATA_FLAG_FIN)); + CU_ASSERT(0 == ud.stream_data.datalen); + + ngtcp2_conn_del(conn); + + /* Re-ordered STREAM frame; we first gets 0 length STREAM frame with + FIN bit set. Then the remaining STREAM frame is received. */ + setup_default_server(&conn); + conn->callbacks.recv_stream_data = recv_stream_data; + conn->user_data = &ud; + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.stream_id = 4; + fr.stream.fin = 1; + fr.stream.offset = 599; + fr.stream.datacnt = 0; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + memset(&ud, 0, sizeof(ud)); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(0 == ud.stream_data.stream_id); + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.stream_id = 4; + fr.stream.fin = 0; + fr.stream.offset = 0; + fr.stream.datacnt = 1; + fr.stream.data[0].len = 599; + fr.stream.data[0].base = null_data; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + memset(&ud, 0, sizeof(ud)); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(4 == ud.stream_data.stream_id); + CU_ASSERT(ud.stream_data.flags & NGTCP2_STREAM_DATA_FLAG_FIN); + CU_ASSERT(599 == ud.stream_data.datalen); + + ngtcp2_conn_del(conn); + + /* Simulate the case where packet is lost. We first gets 0 length + STREAM frame with FIN bit set. Then the lost STREAM frame is + retransmitted with FIN bit set is received. */ + setup_default_server(&conn); + conn->callbacks.recv_stream_data = recv_stream_data; + conn->user_data = &ud; + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.stream_id = 4; + fr.stream.fin = 1; + fr.stream.offset = 599; + fr.stream.datacnt = 0; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + memset(&ud, 0, sizeof(ud)); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(0 == ud.stream_data.stream_id); + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.stream_id = 4; + fr.stream.fin = 1; + fr.stream.offset = 0; + fr.stream.datacnt = 1; + fr.stream.data[0].len = 599; + fr.stream.data[0].base = null_data; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + memset(&ud, 0, sizeof(ud)); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(4 == ud.stream_data.stream_id); + CU_ASSERT(ud.stream_data.flags & NGTCP2_STREAM_DATA_FLAG_FIN); + CU_ASSERT(599 == ud.stream_data.datalen); + + ngtcp2_conn_del(conn); + + /* Receive an unidirectional stream data */ + setup_default_client(&conn); + conn->callbacks.recv_stream_data = recv_stream_data; + conn->user_data = &ud; + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.stream_id = 3; + fr.stream.fin = 0; + fr.stream.offset = 0; + fr.stream.datacnt = 1; + fr.stream.data[0].len = 911; + fr.stream.data[0].base = null_data; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + memset(&ud, 0, sizeof(ud)); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(3 == ud.stream_data.stream_id); + CU_ASSERT(!(ud.stream_data.flags & NGTCP2_STREAM_DATA_FLAG_FIN)); + CU_ASSERT(911 == ud.stream_data.datalen); + + ngtcp2_conn_del(conn); + + /* Receive an unidirectional stream which is beyond the limit. */ + setup_default_server(&conn); + conn->callbacks.recv_stream_data = recv_stream_data; + conn->remote.uni.max_streams = 0; + conn->user_data = &ud; + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.stream_id = 2; + fr.stream.fin = 0; + fr.stream.offset = 0; + fr.stream.datacnt = 1; + fr.stream.data[0].len = 911; + fr.stream.data[0].base = null_data; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + memset(&ud, 0, sizeof(ud)); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(NGTCP2_ERR_STREAM_LIMIT == rv); + + ngtcp2_conn_del(conn); + + /* Receiving nonzero payload for an local unidirectional stream is a + protocol violation. */ + setup_default_client(&conn); + + rv = ngtcp2_conn_open_uni_stream(conn, &stream_id, NULL); + + CU_ASSERT(0 == rv); + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.stream_id = stream_id; + fr.stream.fin = 0; + fr.stream.offset = 0; + fr.stream.datacnt = 1; + fr.stream.data[0].len = 9; + fr.stream.data[0].base = null_data; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(NGTCP2_ERR_STREAM_STATE == rv); + + ngtcp2_conn_del(conn); + + /* DATA on crypto stream, and TLS alert is generated. */ + setup_default_server(&conn); + conn->callbacks.recv_crypto_data = recv_crypto_fatal_alert_generated; + + fr.type = NGTCP2_FRAME_CRYPTO; + fr.crypto.offset = 0; + fr.crypto.datacnt = 1; + fr.crypto.data[0].len = 139; + fr.crypto.data[0].base = null_data; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(NGTCP2_ERR_CRYPTO == rv); + + ngtcp2_conn_del(conn); + + /* 0 length STREAM frame is allowed */ + setup_default_server(&conn); + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.stream_id = 4; + fr.stream.fin = 0; + fr.stream.offset = 0; + fr.stream.datacnt = 0; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, 4)); + + ngtcp2_conn_del(conn); + + /* After sending STOP_SENDING, receiving 2 STREAM frames with fin + bit set must not invoke recv_stream_data callback. */ + setup_default_server(&conn); + conn->callbacks.recv_stream_data = recv_stream_data; + conn->user_data = &ud; + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.stream_id = 4; + fr.stream.fin = 0; + fr.stream.offset = 0; + fr.stream.datacnt = 0; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, 4)); + + rv = ngtcp2_conn_shutdown_stream_read(conn, 4, 99); + + CU_ASSERT(0 == rv); + + for (i = 0; i < 2; ++i) { + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.stream_id = 4; + fr.stream.fin = 1; + fr.stream.offset = 0; + fr.stream.datacnt = 1; + fr.stream.data[0].base = null_data; + fr.stream.data[0].len = 19; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + ud.stream_data.stream_id = 0; + rv = + ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(0 == ud.stream_data.stream_id); + CU_ASSERT(19 == conn->rx.offset); + CU_ASSERT(19 == conn->rx.unsent_max_offset - + conn->local.transport_params.initial_max_data); + CU_ASSERT(conn->local.transport_params.initial_max_data == + conn->rx.max_offset); + } + + ngtcp2_conn_del(conn); + + /* After receiving RESET_STREAM, recv_stream_data callback must not + be invoked */ + setup_default_server(&conn); + conn->callbacks.recv_stream_data = recv_stream_data; + conn->user_data = &ud; + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.stream_id = 0; + fr.stream.fin = 0; + fr.stream.offset = 0; + fr.stream.datacnt = 0; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, 0)); + + fr.type = NGTCP2_FRAME_RESET_STREAM; + fr.reset_stream.stream_id = 0; + fr.reset_stream.app_error_code = 999; + fr.reset_stream.final_size = 199; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(NULL != ngtcp2_conn_find_stream(conn, 0)); + CU_ASSERT(199 == conn->rx.unsent_max_offset - + conn->local.transport_params.initial_max_data); + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.stream_id = 0; + fr.stream.fin = 0; + fr.stream.offset = 0; + fr.stream.datacnt = 1; + fr.stream.data[0].base = null_data; + fr.stream.data[0].len = 198; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + ud.stream_data.stream_id = -1; + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(-1 == ud.stream_data.stream_id); + CU_ASSERT(199 == conn->rx.unsent_max_offset - + conn->local.transport_params.initial_max_data); + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.stream_id = 0; + fr.stream.fin = 1; + fr.stream.offset = 198; + fr.stream.datacnt = 1; + fr.stream.data[0].base = null_data; + fr.stream.data[0].len = 1; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + ud.stream_data.stream_id = -1; + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(-1 == ud.stream_data.stream_id); + CU_ASSERT(199 == conn->rx.unsent_max_offset - + conn->local.transport_params.initial_max_data); + + ngtcp2_conn_del(conn); + + /* ngtcp2_conn_shutdown_stream_read is called in recv_stream_data + callback. Further recv_stream_data callback must not be + called. */ + setup_default_server(&conn); + conn->callbacks.recv_stream_data = recv_stream_data_shutdown_stream_read; + conn->user_data = &ud; + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.stream_id = 4; + fr.stream.fin = 0; + fr.stream.offset = 599; + fr.stream.datacnt = 1; + fr.stream.data[0].len = 1; + fr.stream.data[0].base = null_data; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + memset(&ud, 0, sizeof(ud)); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(0 == ud.stream_data.stream_id); + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.stream_id = 4; + fr.stream.fin = 0; + fr.stream.offset = 0; + fr.stream.datacnt = 1; + fr.stream.data[0].len = 599; + fr.stream.data[0].base = null_data; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + memset(&ud, 0, sizeof(ud)); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(4 == ud.stream_data.stream_id); + CU_ASSERT(!(ud.stream_data.flags & NGTCP2_STREAM_DATA_FLAG_FIN)); + CU_ASSERT(599 == ud.stream_data.datalen); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_recv_ping(void) { + uint8_t buf[1024]; + ngtcp2_conn *conn; + int64_t pkt_num = 133; + ngtcp2_tstamp t = 0; + ngtcp2_frame fr; + size_t pktlen; + int rv; + + setup_default_client(&conn); + + fr.type = NGTCP2_FRAME_PING; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(NULL == conn->pktns.tx.frq); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_recv_max_stream_data(void) { + uint8_t buf[1024]; + ngtcp2_conn *conn; + int64_t pkt_num = 1000000007; + ngtcp2_tstamp t = 0; + ngtcp2_frame fr; + size_t pktlen; + int rv; + ngtcp2_strm *strm; + + /* Receiving MAX_STREAM_DATA to an uninitiated local bidirectional + stream ID is an error */ + setup_default_client(&conn); + + fr.type = NGTCP2_FRAME_MAX_STREAM_DATA; + fr.max_stream_data.stream_id = 4; + fr.max_stream_data.max_stream_data = 8092; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(NGTCP2_ERR_STREAM_STATE == rv); + + ngtcp2_conn_del(conn); + + /* Receiving MAX_STREAM_DATA to an uninitiated local unidirectional + stream ID is an error */ + setup_default_client(&conn); + + fr.type = NGTCP2_FRAME_MAX_STREAM_DATA; + fr.max_stream_data.stream_id = 2; + fr.max_stream_data.max_stream_data = 8092; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(NGTCP2_ERR_STREAM_STATE == rv); + + ngtcp2_conn_del(conn); + + /* Receiving MAX_STREAM_DATA to a remote bidirectional stream which + exceeds limit */ + setup_default_client(&conn); + + fr.type = NGTCP2_FRAME_MAX_STREAM_DATA; + fr.max_stream_data.stream_id = 1; + fr.max_stream_data.max_stream_data = 1000000009; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(NGTCP2_ERR_STREAM_LIMIT == rv); + + ngtcp2_conn_del(conn); + + /* Receiving MAX_STREAM_DATA to a remote bidirectional stream which + the local endpoint has not received yet. */ + setup_default_server(&conn); + + fr.type = NGTCP2_FRAME_MAX_STREAM_DATA; + fr.max_stream_data.stream_id = 4; + fr.max_stream_data.max_stream_data = 1000000009; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + strm = ngtcp2_conn_find_stream(conn, 4); + + CU_ASSERT(NULL != strm); + CU_ASSERT(1000000009 == strm->tx.max_offset); + + ngtcp2_conn_del(conn); + + /* Receiving MAX_STREAM_DATA to a idle remote unidirectional stream + is a protocol violation. */ + setup_default_server(&conn); + + fr.type = NGTCP2_FRAME_MAX_STREAM_DATA; + fr.max_stream_data.stream_id = 2; + fr.max_stream_data.max_stream_data = 1000000009; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(NGTCP2_ERR_STREAM_STATE == rv); + + ngtcp2_conn_del(conn); + + /* Receiving MAX_STREAM_DATA to an existing bidirectional stream */ + setup_default_server(&conn); + + strm = open_stream(conn, 4); + + fr.type = NGTCP2_FRAME_MAX_STREAM_DATA; + fr.max_stream_data.stream_id = 4; + fr.max_stream_data.max_stream_data = 1000000009; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(1000000009 == strm->tx.max_offset); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_send_early_data(void) { + ngtcp2_conn *conn; + ngtcp2_ssize spktlen; + ngtcp2_ssize datalen; + uint8_t buf[1024]; + int64_t stream_id; + int rv; + ngtcp2_tstamp t = 0; + ngtcp2_vec datav; + + setup_early_client(&conn); + + rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + + CU_ASSERT(0 == rv); + + spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), + &datalen, NGTCP2_WRITE_STREAM_FLAG_FIN, + stream_id, null_data, 1024, ++t); + + CU_ASSERT((ngtcp2_ssize)sizeof(buf) == spktlen); + CU_ASSERT(670 == datalen); + + ngtcp2_conn_del(conn); + + /* Verify that Handshake packet and 0-RTT packet are coalesced into + one UDP packet. */ + setup_early_client(&conn); + + rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + + CU_ASSERT(0 == rv); + + spktlen = + ngtcp2_conn_writev_stream(conn, NULL, NULL, buf, sizeof(buf), &datalen, + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + null_datav(&datav, 199), 1, ++t); + + CU_ASSERT(sizeof(buf) == spktlen); + CU_ASSERT(199 == datalen); + + ngtcp2_conn_del(conn); + + /* 0 length 0-RTT packet with FIN bit set */ + setup_early_client(&conn); + + rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + + CU_ASSERT(0 == rv); + + spktlen = ngtcp2_conn_writev_stream(conn, NULL, NULL, buf, sizeof(buf), + &datalen, NGTCP2_WRITE_STREAM_FLAG_FIN, + stream_id, NULL, 0, ++t); + + CU_ASSERT(sizeof(buf) == spktlen); + CU_ASSERT(0 == datalen); + + ngtcp2_conn_del(conn); + + /* Can write 0 length STREAM frame */ + setup_early_client(&conn); + + rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + + CU_ASSERT(0 == rv); + + spktlen = ngtcp2_conn_writev_stream(conn, NULL, NULL, buf, sizeof(buf), + &datalen, NGTCP2_WRITE_STREAM_FLAG_NONE, + -1, NULL, 0, ++t); + + CU_ASSERT(spktlen > 0); + + /* We have written Initial. Now check that STREAM frame is + written. */ + spktlen = ngtcp2_conn_writev_stream(conn, NULL, NULL, buf, sizeof(buf), + &datalen, NGTCP2_WRITE_STREAM_FLAG_NONE, + stream_id, NULL, 0, ++t); + + CU_ASSERT(spktlen > 0); + + ngtcp2_conn_del(conn); + + /* Could not send 0-RTT data because buffer is too small. */ + setup_early_client(&conn); + + rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + + CU_ASSERT(0 == rv); + + spktlen = ngtcp2_conn_writev_stream( + conn, NULL, NULL, buf, + NGTCP2_MIN_LONG_HEADERLEN + 1 + ngtcp2_conn_get_dcid(conn)->datalen + + conn->oscid.datalen + 300, + &datalen, NGTCP2_WRITE_STREAM_FLAG_FIN, stream_id, NULL, 0, ++t); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(-1 == datalen); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_recv_early_data(void) { + ngtcp2_conn *conn; + uint8_t buf[2048]; + size_t pktlen; + ngtcp2_ssize spktlen; + ngtcp2_frame fr; + int64_t pkt_num = 1; + ngtcp2_tstamp t = 0; + ngtcp2_strm *strm; + ngtcp2_cid rcid; + int rv; + my_user_data ud; + + rcid_init(&rcid); + + setup_early_server(&conn); + conn->callbacks.recv_stream_data = recv_stream_data; + conn->user_data = &ud; + + fr.type = NGTCP2_FRAME_CRYPTO; + fr.crypto.offset = 0; + fr.crypto.datacnt = 1; + fr.crypto.data[0].len = 1221; + fr.crypto.data[0].base = null_data; + + pktlen = write_initial_pkt( + buf, sizeof(buf), &rcid, ngtcp2_conn_get_dcid(conn), ++pkt_num, + conn->client_chosen_version, NULL, 0, &fr, 1, &null_ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.stream_id = 4; + fr.stream.fin = 1; + fr.stream.offset = 0; + fr.stream.datacnt = 1; + fr.stream.data[0].len = 911; + fr.stream.data[0].base = null_data; + + pktlen = + write_0rtt_pkt(buf, sizeof(buf), &rcid, ngtcp2_conn_get_dcid(conn), + ++pkt_num, conn->client_chosen_version, &fr, 1, &null_ckm); + + memset(&ud, 0, sizeof(ud)); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(4 == ud.stream_data.stream_id); + CU_ASSERT(ud.stream_data.flags & NGTCP2_STREAM_DATA_FLAG_EARLY); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + + strm = ngtcp2_conn_find_stream(conn, 4); + + CU_ASSERT(NULL != strm); + CU_ASSERT(911 == strm->rx.last_offset); + + ngtcp2_conn_del(conn); + + /* Re-ordered 0-RTT packet */ + setup_early_server(&conn); + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.stream_id = 4; + fr.stream.fin = 1; + fr.stream.offset = 0; + fr.stream.datacnt = 1; + fr.stream.data[0].len = 119; + fr.stream.data[0].base = null_data; + + pktlen = + write_0rtt_pkt(buf, sizeof(buf), &rcid, ngtcp2_conn_get_dcid(conn), + ++pkt_num, conn->client_chosen_version, &fr, 1, &null_ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(NGTCP2_ERR_RETRY == rv); + + ngtcp2_conn_del(conn); + + /* Compound packet */ + setup_early_server(&conn); + + fr.type = NGTCP2_FRAME_CRYPTO; + fr.crypto.offset = 0; + fr.crypto.datacnt = 1; + fr.crypto.data[0].len = 111; + fr.crypto.data[0].base = null_data; + + pktlen = write_initial_pkt( + buf, sizeof(buf), &rcid, ngtcp2_conn_get_dcid(conn), ++pkt_num, + conn->client_chosen_version, NULL, 0, &fr, 1, &null_ckm); + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.stream_id = 4; + fr.stream.fin = 1; + fr.stream.offset = 0; + fr.stream.datacnt = 1; + fr.stream.data[0].len = 999; + fr.stream.data[0].base = null_data; + + pktlen += write_0rtt_pkt(buf + pktlen, sizeof(buf) - pktlen, &rcid, + ngtcp2_conn_get_dcid(conn), ++pkt_num, + conn->client_chosen_version, &fr, 1, &null_ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + + strm = ngtcp2_conn_find_stream(conn, 4); + + CU_ASSERT(NULL != strm); + CU_ASSERT(999 == strm->rx.last_offset); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_recv_compound_pkt(void) { + ngtcp2_conn *conn; + uint8_t buf[2048]; + size_t pktlen; + ngtcp2_ssize spktlen; + ngtcp2_frame fr; + int64_t pkt_num = 1; + ngtcp2_tstamp t = 0; + ngtcp2_acktr_entry *ackent; + int rv; + ngtcp2_ksl_it it; + + /* 2 QUIC long packets in one UDP packet */ + setup_handshake_server(&conn); + + fr.type = NGTCP2_FRAME_CRYPTO; + fr.crypto.offset = 0; + fr.crypto.datacnt = 1; + fr.crypto.data[0].len = 611; + fr.crypto.data[0].base = null_data; + + pktlen = write_initial_pkt( + buf, sizeof(buf), &conn->oscid, ngtcp2_conn_get_dcid(conn), ++pkt_num, + conn->client_chosen_version, NULL, 0, &fr, 1, &null_ckm); + + pktlen += write_initial_pkt(buf + pktlen, sizeof(buf) - pktlen, &conn->oscid, + ngtcp2_conn_get_dcid(conn), ++pkt_num, + conn->client_chosen_version, NULL, 0, &fr, 1, + &null_ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + + it = ngtcp2_acktr_get(&conn->in_pktns->acktr); + ackent = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(pkt_num == ackent->pkt_num); + CU_ASSERT(2 == ackent->len); + + ngtcp2_ksl_it_next(&it); + + CU_ASSERT(ngtcp2_ksl_it_end(&it)); + + ngtcp2_conn_del(conn); + + /* 1 long packet and 1 short packet in one UDP packet */ + setup_default_server(&conn); + + fr.type = NGTCP2_FRAME_PADDING; + fr.padding.len = 1; + + pktlen = write_handshake_pkt(buf, sizeof(buf), &conn->oscid, + ngtcp2_conn_get_dcid(conn), ++pkt_num, + conn->client_chosen_version, &fr, 1, &null_ckm); + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.stream_id = 4; + fr.stream.fin = 0; + fr.stream.offset = 0; + fr.stream.datacnt = 1; + fr.stream.data[0].len = 426; + fr.stream.data[0].base = null_data; + + pktlen += write_pkt(buf + pktlen, sizeof(buf) - pktlen, &conn->oscid, + ++pkt_num, &fr, 1, conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + it = ngtcp2_acktr_get(&conn->pktns.acktr); + ackent = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(ackent->pkt_num == pkt_num); + + it = ngtcp2_acktr_get(&conn->hs_pktns->acktr); + + CU_ASSERT(!ngtcp2_ksl_it_end(&it)); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_pkt_payloadlen(void) { + ngtcp2_conn *conn; + uint8_t buf[2048]; + size_t pktlen; + ngtcp2_frame fr; + int64_t pkt_num = 1; + ngtcp2_tstamp t = 0; + uint64_t payloadlen; + int rv; + const ngtcp2_cid *dcid; + + /* Payload length is invalid */ + setup_handshake_server(&conn); + + fr.type = NGTCP2_FRAME_CRYPTO; + fr.crypto.offset = 0; + fr.crypto.datacnt = 1; + fr.crypto.data[0].len = 1231; + fr.crypto.data[0].base = null_data; + + dcid = ngtcp2_conn_get_dcid(conn); + + pktlen = write_initial_pkt(buf, sizeof(buf), &conn->oscid, dcid, ++pkt_num, + conn->client_chosen_version, NULL, 0, &fr, 1, + &null_ckm); + + payloadlen = read_pkt_payloadlen(buf, dcid, &conn->oscid); + write_pkt_payloadlen(buf, dcid, &conn->oscid, payloadlen + 1); + + /* This first packet which does not increase initial packet number + space CRYPTO offset or it does not get buffered as 0RTT is an + error. But it is unsecured Initial, so we just ignore it. */ + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(NGTCP2_ERR_DROP_CONN == rv); + CU_ASSERT(NGTCP2_CS_SERVER_INITIAL == conn->state); + + ngtcp2_conn_del(conn); + + /* Client Initial packet included in UDP datagram smaller than 1200 + is discarded. */ + setup_handshake_server(&conn); + + fr.type = NGTCP2_FRAME_CRYPTO; + fr.crypto.offset = 0; + fr.crypto.datacnt = 1; + fr.crypto.data[0].len = 1000; + fr.crypto.data[0].base = null_data; + + pktlen = write_initial_pkt(buf, sizeof(buf), &conn->oscid, + ngtcp2_conn_get_dcid(conn), 0, NGTCP2_PROTO_VER_V1, + NULL, 0, &fr, 1, &null_ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(NGTCP2_ERR_DROP_CONN == rv); + CU_ASSERT(NGTCP2_CS_SERVER_INITIAL == conn->state); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_writev_stream(void) { + ngtcp2_conn *conn; + uint8_t buf[2048]; + ngtcp2_ssize spktlen; + ngtcp2_tstamp t = 0; + int rv; + int64_t stream_id; + ngtcp2_vec datav = {null_data, 10}; + ngtcp2_ssize datalen; + size_t left; + + /* 0 length STREAM should not be written if we supply nonzero length + data. */ + setup_default_client(&conn); + + /* This will sends NEW_CONNECTION_ID frames */ + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + + rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + + CU_ASSERT(0 == rv); + + /* + * Long header (1+18+1) + * STREAM overhead (+3) + * AEAD overhead (16) + */ + spktlen = ngtcp2_conn_writev_stream(conn, NULL, NULL, buf, 39, &datalen, + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + &datav, 1, ++t); + + CU_ASSERT(0 == spktlen); + CU_ASSERT(-1 == datalen); + + ngtcp2_conn_del(conn); + + /* +1 buffer size */ + setup_default_client(&conn); + + /* This will sends NEW_CONNECTION_ID frames */ + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + + rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + + CU_ASSERT(0 == rv); + + spktlen = ngtcp2_conn_writev_stream(conn, NULL, NULL, buf, 40, &datalen, + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + &datav, 1, ++t); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(1 == datalen); + + ngtcp2_conn_del(conn); + + /* Coalesces multiple STREAM frames */ + setup_default_client(&conn); + conn->local.bidi.max_streams = 100; + + rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + + CU_ASSERT(0 == rv); + + spktlen = ngtcp2_conn_writev_stream(conn, NULL, NULL, buf, 1200, &datalen, + NGTCP2_WRITE_STREAM_FLAG_MORE, stream_id, + &datav, 1, ++t); + + CU_ASSERT(NGTCP2_ERR_WRITE_MORE == spktlen); + CU_ASSERT(10 == datalen); + + left = ngtcp2_ppe_left(&conn->pkt.ppe); + + rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + + CU_ASSERT(0 == rv); + + spktlen = ngtcp2_conn_writev_stream(conn, NULL, NULL, buf, 1200, &datalen, + NGTCP2_WRITE_STREAM_FLAG_MORE, stream_id, + &datav, 1, ++t); + + CU_ASSERT(NGTCP2_ERR_WRITE_MORE == spktlen); + CU_ASSERT(10 == datalen); + CU_ASSERT(ngtcp2_ppe_left(&conn->pkt.ppe) < left); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + + ngtcp2_conn_del(conn); + + /* 0RTT: Coalesces multiple STREAM frames */ + setup_early_client(&conn); + conn->local.bidi.max_streams = 100; + + rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + + CU_ASSERT(0 == rv); + + spktlen = ngtcp2_conn_writev_stream(conn, NULL, NULL, buf, 1200, &datalen, + NGTCP2_WRITE_STREAM_FLAG_MORE, stream_id, + &datav, 1, ++t); + + CU_ASSERT(NGTCP2_ERR_WRITE_MORE == spktlen); + CU_ASSERT(10 == datalen); + + left = ngtcp2_ppe_left(&conn->pkt.ppe); + + rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + + CU_ASSERT(0 == rv); + + spktlen = ngtcp2_conn_writev_stream(conn, NULL, NULL, buf, 1200, &datalen, + NGTCP2_WRITE_STREAM_FLAG_MORE, stream_id, + &datav, 1, ++t); + + CU_ASSERT(NGTCP2_ERR_WRITE_MORE == spktlen); + CU_ASSERT(10 == datalen); + CU_ASSERT(ngtcp2_ppe_left(&conn->pkt.ppe) < left); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + /* Make sure that packet is padded */ + CU_ASSERT(1200 == spktlen); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_writev_datagram(void) { + ngtcp2_conn *conn; + uint8_t buf[2048]; + ngtcp2_ssize spktlen; + ngtcp2_tstamp t = 0; + ngtcp2_vec datav = {null_data, 10}; + ngtcp2_vec vec; + int accepted; + my_user_data ud; + ngtcp2_frame fr; + size_t pktlen; + int rv; + + setup_default_client(&conn); + conn->callbacks.ack_datagram = ack_datagram; + conn->remote.transport_params->max_datagram_frame_size = 1 + 1 + 10; + conn->user_data = &ud; + + spktlen = ngtcp2_conn_writev_datagram( + conn, NULL, NULL, buf, sizeof(buf), &accepted, + NGTCP2_WRITE_DATAGRAM_FLAG_NONE, 1000000009, &datav, 1, ++t); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(0 != accepted); + + fr.type = NGTCP2_FRAME_ACK; + fr.ack.largest_ack = conn->pktns.tx.last_pkt_num; + fr.ack.ack_delay = 0; + fr.ack.first_ack_range = 0; + fr.ack.rangecnt = 0; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 0, &fr, 1, + conn->pktns.crypto.rx.ckm); + + ud.datagram.dgram_id = 0; + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(1000000009 == ud.datagram.dgram_id); + + ngtcp2_conn_del(conn); + + /* Coalesces multiple DATAGRAM frames into a single QUIC packet */ + setup_default_client(&conn); + conn->remote.transport_params->max_datagram_frame_size = 65535; + + spktlen = ngtcp2_conn_writev_datagram( + conn, NULL, NULL, buf, sizeof(buf), &accepted, + NGTCP2_WRITE_DATAGRAM_FLAG_MORE, 1000000007, &datav, 1, ++t); + + CU_ASSERT(NGTCP2_ERR_WRITE_MORE == spktlen); + CU_ASSERT(0 != accepted); + + spktlen = ngtcp2_conn_writev_datagram( + conn, NULL, NULL, buf, sizeof(buf), &accepted, + NGTCP2_WRITE_DATAGRAM_FLAG_MORE, 1000000007, &datav, 1, ++t); + + CU_ASSERT(NGTCP2_ERR_WRITE_MORE == spktlen); + CU_ASSERT(0 != accepted); + + spktlen = ngtcp2_conn_writev_datagram( + conn, NULL, NULL, buf, sizeof(buf), &accepted, + NGTCP2_WRITE_DATAGRAM_FLAG_NONE, 0, &datav, 1, ++t); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(0 != accepted); + + ngtcp2_conn_del(conn); + + /* DATAGRAM cannot fit into QUIC packet because the other frames + occupy the space */ + setup_default_client(&conn); + conn->remote.transport_params->max_datagram_frame_size = + 1 + ngtcp2_put_uvarintlen(2000) + 2000; + + vec.base = null_data; + vec.len = 2000; + + spktlen = ngtcp2_conn_writev_datagram( + conn, NULL, NULL, buf, sizeof(buf), &accepted, + NGTCP2_WRITE_DATAGRAM_FLAG_NONE, 987, &vec, 1, ++t); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(0 == accepted); + + spktlen = ngtcp2_conn_writev_datagram( + conn, NULL, NULL, buf, sizeof(buf), &accepted, + NGTCP2_WRITE_DATAGRAM_FLAG_NONE, 545, &vec, 1, ++t); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(0 != accepted); + + ngtcp2_conn_del(conn); + + /* Calling ngtcp2_conn_writev_datagram without receiving positive + max_datagram_frame_size is an error */ + setup_default_client(&conn); + + spktlen = ngtcp2_conn_writev_datagram( + conn, NULL, NULL, buf, sizeof(buf), &accepted, + NGTCP2_WRITE_DATAGRAM_FLAG_NONE, 999, &datav, 1, ++t); + + CU_ASSERT(NGTCP2_ERR_INVALID_STATE == spktlen); + + ngtcp2_conn_del(conn); + + /* Sending DATAGRAM which is larger than the value of received + max_datagram_frame_size is an error */ + setup_default_client(&conn); + conn->remote.transport_params->max_datagram_frame_size = 9; + + spktlen = ngtcp2_conn_writev_datagram( + conn, NULL, NULL, buf, sizeof(buf), &accepted, + NGTCP2_WRITE_DATAGRAM_FLAG_NONE, 4433, &datav, 1, ++t); + + CU_ASSERT(NGTCP2_ERR_INVALID_ARGUMENT == spktlen); + + ngtcp2_conn_del(conn); + + /* Send DATAGRAM frame in a 0RTT packet */ + setup_early_client(&conn); + + conn->remote.transport_params->max_datagram_frame_size = 4311; + + spktlen = ngtcp2_conn_writev_datagram( + conn, NULL, NULL, buf, sizeof(buf), &accepted, + NGTCP2_WRITE_DATAGRAM_FLAG_NONE, 22360679, &datav, 1, ++t); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(0 != accepted); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_recv_datagram(void) { + ngtcp2_conn *conn; + uint8_t buf[2048]; + ngtcp2_frame fr; + size_t pktlen; + int64_t pkt_num = 0; + ngtcp2_tstamp t = 0; + my_user_data ud; + int rv; + ngtcp2_cid rcid; + + rcid_init(&rcid); + + setup_default_server(&conn); + conn->user_data = &ud; + conn->callbacks.recv_datagram = recv_datagram; + conn->local.transport_params.max_datagram_frame_size = 1 + 1111; + + fr.type = NGTCP2_FRAME_DATAGRAM; + fr.datagram.data = fr.datagram.rdata; + fr.datagram.data->base = null_data; + fr.datagram.data->len = 1111; + fr.datagram.datacnt = 1; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + memset(&ud, 0, sizeof(ud)); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(1111 == ud.datagram.datalen); + CU_ASSERT(!(NGTCP2_DATAGRAM_FLAG_EARLY & ud.datagram.flags)); + + ngtcp2_conn_del(conn); + + /* Receiving DATAGRAM frame which is strictly larger than the + declared limit is an error */ + setup_default_server(&conn); + conn->local.transport_params.max_datagram_frame_size = 1 + 1111 - 1; + + fr.type = NGTCP2_FRAME_DATAGRAM; + fr.datagram.data = fr.datagram.rdata; + fr.datagram.data->base = null_data; + fr.datagram.data->len = 1111; + fr.datagram.datacnt = 1; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(NGTCP2_ERR_PROTO == rv); + + ngtcp2_conn_del(conn); + + /* Receiving DATAGRAM frame in a 0RTT packet */ + setup_early_server(&conn); + conn->user_data = &ud; + conn->callbacks.recv_datagram = recv_datagram; + conn->local.transport_params.max_datagram_frame_size = 1 + 1111; + + fr.type = NGTCP2_FRAME_CRYPTO; + fr.crypto.offset = 0; + fr.crypto.datacnt = 1; + fr.crypto.data[0].len = 1199; + fr.crypto.data[0].base = null_data; + + pktlen = write_initial_pkt( + buf, sizeof(buf), &rcid, ngtcp2_conn_get_dcid(conn), ++pkt_num, + conn->client_chosen_version, NULL, 0, &fr, 1, &null_ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + fr.type = NGTCP2_FRAME_DATAGRAM; + fr.datagram.data = fr.datagram.rdata; + fr.datagram.data->base = null_data; + fr.datagram.data->len = 1111; + fr.datagram.datacnt = 1; + + pktlen = + write_0rtt_pkt(buf, sizeof(buf), &rcid, ngtcp2_conn_get_dcid(conn), + ++pkt_num, conn->client_chosen_version, &fr, 1, &null_ckm); + + memset(&ud, 0, sizeof(ud)); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(1111 == ud.datagram.datalen); + CU_ASSERT(NGTCP2_DATAGRAM_FLAG_EARLY & ud.datagram.flags); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_recv_new_connection_id(void) { + ngtcp2_conn *conn; + uint8_t buf[2048]; + size_t pktlen; + ngtcp2_ssize spktlen; + ngtcp2_tstamp t = 0; + int64_t pkt_num = 0; + ngtcp2_frame fr; + ngtcp2_frame frs[16]; + const uint8_t cid[] = {0xf0, 0xf1, 0xf2, 0xf3}; + const uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN] = {0xff}; + const uint8_t cid2[] = {0xf0, 0xf1, 0xf2, 0xf4}; + const uint8_t token2[NGTCP2_STATELESS_RESET_TOKENLEN] = {0xfe}; + const uint8_t cid3[] = {0xf0, 0xf1, 0xf2, 0xf5}; + const uint8_t token3[NGTCP2_STATELESS_RESET_TOKENLEN] = {0xfd}; + ngtcp2_dcid *dcid; + int rv; + ngtcp2_frame_chain *frc; + size_t i; + + setup_default_client(&conn); + + /* This will send NEW_CONNECTION_ID frames */ + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + + fr.type = NGTCP2_FRAME_NEW_CONNECTION_ID; + fr.new_connection_id.seq = 1; + fr.new_connection_id.retire_prior_to = 0; + ngtcp2_cid_init(&fr.new_connection_id.cid, cid, sizeof(cid)); + memcpy(fr.new_connection_id.stateless_reset_token, token, sizeof(token)); + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(1 == ngtcp2_ringbuf_len(&conn->dcid.unused.rb)); + + assert(ngtcp2_ringbuf_len(&conn->dcid.unused.rb)); + dcid = ngtcp2_ringbuf_get(&conn->dcid.unused.rb, 0); + + CU_ASSERT(ngtcp2_cid_eq(&fr.new_connection_id.cid, &dcid->cid)); + CU_ASSERT(dcid->flags & NGTCP2_DCID_FLAG_TOKEN_PRESENT); + CU_ASSERT(0 == memcmp(fr.new_connection_id.stateless_reset_token, dcid->token, + sizeof(fr.new_connection_id.stateless_reset_token))); + + fr.type = NGTCP2_FRAME_NEW_CONNECTION_ID; + fr.new_connection_id.seq = 2; + fr.new_connection_id.retire_prior_to = 2; + ngtcp2_cid_init(&fr.new_connection_id.cid, cid2, sizeof(cid2)); + memcpy(fr.new_connection_id.stateless_reset_token, token2, sizeof(token2)); + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(0 == ngtcp2_ringbuf_len(&conn->dcid.bound.rb)); + CU_ASSERT(0 == ngtcp2_ringbuf_len(&conn->dcid.unused.rb)); + CU_ASSERT(2 == conn->dcid.current.seq); + CU_ASSERT(NULL != conn->pktns.tx.frq); + CU_ASSERT(2 == conn->dcid.retire_prior_to); + + frc = conn->pktns.tx.frq; + + CU_ASSERT(NGTCP2_FRAME_RETIRE_CONNECTION_ID == frc->fr.type); + + frc = frc->next; + + CU_ASSERT(NGTCP2_FRAME_RETIRE_CONNECTION_ID == frc->fr.type); + CU_ASSERT(NULL == frc->next); + + /* This will send RETIRE_CONNECTION_ID frames */ + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + + ngtcp2_conn_del(conn); + + /* Received connection ID is immediately retired due to packet + reordering */ + setup_default_client(&conn); + + /* This will send NEW_CONNECTION_ID frames */ + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + + fr.type = NGTCP2_FRAME_NEW_CONNECTION_ID; + fr.new_connection_id.seq = 2; + fr.new_connection_id.retire_prior_to = 2; + ngtcp2_cid_init(&fr.new_connection_id.cid, cid, sizeof(cid)); + memcpy(fr.new_connection_id.stateless_reset_token, token, sizeof(token)); + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(0 == ngtcp2_ringbuf_len(&conn->dcid.unused.rb)); + CU_ASSERT(2 == conn->dcid.current.seq); + CU_ASSERT(2 == conn->dcid.retire_prior_to); + + frc = conn->pktns.tx.frq; + + CU_ASSERT(NGTCP2_FRAME_RETIRE_CONNECTION_ID == frc->fr.type); + CU_ASSERT(NULL == frc->next); + + /* This will send RETIRE_CONNECTION_ID frames */ + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + + fr.type = NGTCP2_FRAME_NEW_CONNECTION_ID; + fr.new_connection_id.seq = 1; + fr.new_connection_id.retire_prior_to = 0; + ngtcp2_cid_init(&fr.new_connection_id.cid, cid2, sizeof(cid2)); + memcpy(fr.new_connection_id.stateless_reset_token, token2, sizeof(token2)); + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(0 == ngtcp2_ringbuf_len(&conn->dcid.unused.rb)); + CU_ASSERT(2 == conn->dcid.current.seq); + CU_ASSERT(2 == conn->dcid.retire_prior_to); + + frc = conn->pktns.tx.frq; + + CU_ASSERT(NGTCP2_FRAME_RETIRE_CONNECTION_ID == frc->fr.type); + CU_ASSERT(NULL == frc->next); + + ngtcp2_conn_del(conn); + + /* ngtcp2_pv contains DCIDs that should be retired. */ + setup_default_server(&conn); + + /* This will send NEW_CONNECTION_ID frames */ + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + + assert(NULL == conn->pv); + + frs[0].type = NGTCP2_FRAME_PING; + frs[1].type = NGTCP2_FRAME_NEW_CONNECTION_ID; + frs[1].new_connection_id.seq = 1; + frs[1].new_connection_id.retire_prior_to = 0; + ngtcp2_cid_init(&frs[1].new_connection_id.cid, cid, sizeof(cid)); + memcpy(frs[1].new_connection_id.stateless_reset_token, token, sizeof(token)); + frs[2].type = NGTCP2_FRAME_NEW_CONNECTION_ID; + frs[2].new_connection_id.seq = 2; + frs[2].new_connection_id.retire_prior_to = 0; + ngtcp2_cid_init(&frs[2].new_connection_id.cid, cid2, sizeof(cid2)); + memcpy(frs[2].new_connection_id.stateless_reset_token, token2, + sizeof(token2)); + frs[3].type = NGTCP2_FRAME_NEW_CONNECTION_ID; + frs[3].new_connection_id.seq = 3; + frs[3].new_connection_id.retire_prior_to = 0; + ngtcp2_cid_init(&frs[3].new_connection_id.cid, cid3, sizeof(cid3)); + memcpy(frs[3].new_connection_id.stateless_reset_token, token3, + sizeof(token3)); + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 4, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &new_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + assert(NULL != conn->pv); + + CU_ASSERT(conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE); + CU_ASSERT(1 == conn->pv->dcid.seq); + CU_ASSERT(0 == conn->pv->fallback_dcid.seq); + CU_ASSERT(2 == ngtcp2_ringbuf_len(&conn->dcid.unused.rb)); + + fr.type = NGTCP2_FRAME_NEW_CONNECTION_ID; + fr.new_connection_id.seq = 3; + fr.new_connection_id.retire_prior_to = 2; + ngtcp2_cid_init(&fr.new_connection_id.cid, cid3, sizeof(cid3)); + memcpy(fr.new_connection_id.stateless_reset_token, token3, sizeof(token3)); + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &new_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(0 == ngtcp2_ringbuf_len(&conn->dcid.unused.rb)); + CU_ASSERT(conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE); + CU_ASSERT(2 == conn->pv->dcid.seq); + CU_ASSERT(3 == conn->pv->fallback_dcid.seq); + + frc = conn->pktns.tx.frq; + + CU_ASSERT(NGTCP2_FRAME_RETIRE_CONNECTION_ID == frc->fr.type); + CU_ASSERT(0 == frc->fr.retire_connection_id.seq); + frc = frc->next; + + CU_ASSERT(NGTCP2_FRAME_RETIRE_CONNECTION_ID == frc->fr.type); + CU_ASSERT(1 == frc->fr.retire_connection_id.seq); + CU_ASSERT(NULL == frc->next); + + ngtcp2_conn_del(conn); + + /* ngtcp2_pv contains DCID in fallback that should be retired and + there is not enough connection ID left. */ + setup_default_server(&conn); + + /* This will send NEW_CONNECTION_ID frames */ + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + + assert(NULL == conn->pv); + + frs[0].type = NGTCP2_FRAME_PING; + frs[1].type = NGTCP2_FRAME_NEW_CONNECTION_ID; + frs[1].new_connection_id.seq = 1; + frs[1].new_connection_id.retire_prior_to = 0; + ngtcp2_cid_init(&frs[1].new_connection_id.cid, cid, sizeof(cid)); + memcpy(frs[1].new_connection_id.stateless_reset_token, token, sizeof(token)); + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 2, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &new_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + assert(NULL != conn->pv); + + CU_ASSERT(conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE); + CU_ASSERT(1 == conn->pv->dcid.seq); + CU_ASSERT(0 == conn->pv->fallback_dcid.seq); + CU_ASSERT(0 == ngtcp2_ringbuf_len(&conn->dcid.unused.rb)); + + fr.type = NGTCP2_FRAME_NEW_CONNECTION_ID; + fr.new_connection_id.seq = 2; + fr.new_connection_id.retire_prior_to = 2; + ngtcp2_cid_init(&fr.new_connection_id.cid, cid2, sizeof(cid2)); + memcpy(fr.new_connection_id.stateless_reset_token, token2, sizeof(token2)); + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &new_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(2 == conn->dcid.current.seq); + CU_ASSERT(0 == ngtcp2_ringbuf_len(&conn->dcid.unused.rb)); + CU_ASSERT(NULL == conn->pv); + + frc = conn->pktns.tx.frq; + + CU_ASSERT(NGTCP2_FRAME_RETIRE_CONNECTION_ID == frc->fr.type); + CU_ASSERT(0 == frc->fr.retire_connection_id.seq); + + frc = frc->next; + + CU_ASSERT(NGTCP2_FRAME_RETIRE_CONNECTION_ID == frc->fr.type); + CU_ASSERT(1 == frc->fr.retire_connection_id.seq); + CU_ASSERT(NULL == frc->next); + + ngtcp2_conn_del(conn); + + /* ngtcp2_pv contains DCIDs that should be retired and there is not + enough connection ID left to continue path validation. */ + setup_default_server(&conn); + + /* This will send NEW_CONNECTION_ID frames */ + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + + assert(NULL == conn->pv); + + frs[0].type = NGTCP2_FRAME_PING; + frs[1].type = NGTCP2_FRAME_NEW_CONNECTION_ID; + frs[1].new_connection_id.seq = 1; + frs[1].new_connection_id.retire_prior_to = 0; + ngtcp2_cid_init(&frs[1].new_connection_id.cid, cid, sizeof(cid)); + memcpy(frs[1].new_connection_id.stateless_reset_token, token, sizeof(token)); + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 2, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &new_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + assert(NULL != conn->pv); + + CU_ASSERT(conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE); + CU_ASSERT(1 == conn->pv->dcid.seq); + CU_ASSERT(0 == conn->pv->fallback_dcid.seq); + CU_ASSERT(0 == ngtcp2_ringbuf_len(&conn->dcid.unused.rb)); + + /* Overwrite seq in pv->dcid so that pv->dcid cannot be renewed. */ + conn->pv->dcid.seq = 2; + /* Internally we assume that if primary dcid and pv->dcid differ, + then no fallback dcid is present. */ + conn->pv->flags &= (uint8_t)~NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE; + + fr.type = NGTCP2_FRAME_NEW_CONNECTION_ID; + fr.new_connection_id.seq = 3; + fr.new_connection_id.retire_prior_to = 3; + ngtcp2_cid_init(&fr.new_connection_id.cid, cid3, sizeof(cid3)); + memcpy(fr.new_connection_id.stateless_reset_token, token3, sizeof(token3)); + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &new_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(3 == conn->dcid.current.seq); + CU_ASSERT(0 == ngtcp2_ringbuf_len(&conn->dcid.unused.rb)); + CU_ASSERT(NULL == conn->pv); + + frc = conn->pktns.tx.frq; + + CU_ASSERT(NGTCP2_FRAME_RETIRE_CONNECTION_ID == frc->fr.type); + CU_ASSERT(2 == frc->fr.retire_connection_id.seq); + + frc = frc->next; + + CU_ASSERT(NGTCP2_FRAME_RETIRE_CONNECTION_ID == frc->fr.type); + CU_ASSERT(1 == frc->fr.retire_connection_id.seq); + CU_ASSERT(NULL == frc->next); + + ngtcp2_conn_del(conn); + + /* Receiving more than advertised CID is treated as error */ + setup_default_server(&conn); + conn->local.transport_params.active_connection_id_limit = 2; + + /* This will send NEW_CONNECTION_ID frames */ + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + + assert(NULL == conn->pv); + + frs[0].type = NGTCP2_FRAME_NEW_CONNECTION_ID; + frs[0].new_connection_id.seq = 1; + frs[0].new_connection_id.retire_prior_to = 0; + ngtcp2_cid_init(&frs[0].new_connection_id.cid, cid, sizeof(cid)); + memcpy(frs[0].new_connection_id.stateless_reset_token, token, sizeof(token)); + frs[1].type = NGTCP2_FRAME_NEW_CONNECTION_ID; + frs[1].new_connection_id.seq = 2; + frs[1].new_connection_id.retire_prior_to = 0; + ngtcp2_cid_init(&frs[1].new_connection_id.cid, cid2, sizeof(cid2)); + memcpy(frs[1].new_connection_id.stateless_reset_token, token2, + sizeof(token2)); + frs[2].type = NGTCP2_FRAME_NEW_CONNECTION_ID; + frs[2].new_connection_id.seq = 3; + frs[2].new_connection_id.retire_prior_to = 0; + ngtcp2_cid_init(&frs[2].new_connection_id.cid, cid3, sizeof(cid3)); + memcpy(frs[2].new_connection_id.stateless_reset_token, token3, + sizeof(token3)); + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 3, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &new_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(NGTCP2_ERR_CONNECTION_ID_LIMIT == rv); + + ngtcp2_conn_del(conn); + + /* Receiving duplicated NEW_CONNECTION_ID frame */ + setup_default_server(&conn); + + /* This will send NEW_CONNECTION_ID frames */ + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + + frs[0].type = NGTCP2_FRAME_PING; + + frs[1].type = NGTCP2_FRAME_NEW_CONNECTION_ID; + frs[1].new_connection_id.seq = 1; + frs[1].new_connection_id.retire_prior_to = 1; + ngtcp2_cid_init(&frs[1].new_connection_id.cid, cid, sizeof(cid)); + memcpy(frs[1].new_connection_id.stateless_reset_token, token, sizeof(token)); + + frs[2].type = NGTCP2_FRAME_NEW_CONNECTION_ID; + frs[2].new_connection_id.seq = 2; + frs[2].new_connection_id.retire_prior_to = 1; + ngtcp2_cid_init(&frs[2].new_connection_id.cid, cid2, sizeof(cid2)); + memcpy(frs[2].new_connection_id.stateless_reset_token, token2, + sizeof(token2)); + + frs[3].type = NGTCP2_FRAME_PADDING; + frs[3].padding.len = 1200; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 4, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &new_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(0 == ngtcp2_ringbuf_len(&conn->dcid.unused.rb)); + CU_ASSERT(2 == conn->dcid.current.seq); + CU_ASSERT(NULL != conn->pv); + CU_ASSERT(ngtcp2_cid_eq(&frs[1].new_connection_id.cid, + &conn->pv->fallback_dcid.cid)); + + /* This will send PATH_CHALLENGE frame */ + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen >= 1200); + + fr.type = NGTCP2_FRAME_PATH_RESPONSE; + memset(fr.path_response.data, 0, sizeof(fr.path_response.data)); + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &new_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + /* Server starts probing old path */ + CU_ASSERT(NULL != conn->pv); + CU_ASSERT(ngtcp2_path_eq(&null_path.path, &conn->pv->dcid.ps.path)); + + /* Receive NEW_CONNECTION_ID seq=1 again, which should be ignored. */ + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 2, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &new_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(0 == ngtcp2_ringbuf_len(&conn->dcid.unused.rb)); + CU_ASSERT(2 == conn->dcid.current.seq); + + ngtcp2_conn_del(conn); + + /* Exceeding the limit for the number of unacknowledged + RETIRE_CONNECTION_ID leads to NGTCP2_ERR_CONNECTION_ID_LIMIT. */ + setup_default_server(&conn); + + /* This will send NEW_CONNECTION_ID frames */ + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + + for (i = 0; i < 7; ++i) { + frs[i].type = NGTCP2_FRAME_NEW_CONNECTION_ID; + frs[i].new_connection_id.seq = i + 1; + frs[i].new_connection_id.retire_prior_to = 0; + ngtcp2_cid_init(&frs[i].new_connection_id.cid, cid, sizeof(cid)); + frs[i].new_connection_id.cid.data[0] = (uint8_t)i; + memcpy(frs[i].new_connection_id.stateless_reset_token, token, + sizeof(token)); + } + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 7, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + for (i = 0; i < 8; ++i) { + frs[i].type = NGTCP2_FRAME_NEW_CONNECTION_ID; + frs[i].new_connection_id.seq = i + 8; + frs[i].new_connection_id.retire_prior_to = 8; + ngtcp2_cid_init(&frs[i].new_connection_id.cid, cid, sizeof(cid)); + frs[i].new_connection_id.cid.data[0] = (uint8_t)(i + 8); + memcpy(frs[i].new_connection_id.stateless_reset_token, token, + sizeof(token)); + } + + for (i = 0; i < 8; ++i) { + frs[i + 8].type = NGTCP2_FRAME_NEW_CONNECTION_ID; + frs[i + 8].new_connection_id.seq = i + 16; + frs[i + 8].new_connection_id.retire_prior_to = 16; + ngtcp2_cid_init(&frs[i + 8].new_connection_id.cid, cid, sizeof(cid)); + frs[i + 8].new_connection_id.cid.data[0] = (uint8_t)(i + 16); + memcpy(frs[i + 8].new_connection_id.stateless_reset_token, token, + sizeof(token)); + } + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 16, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + frs[0].type = NGTCP2_FRAME_NEW_CONNECTION_ID; + frs[0].new_connection_id.seq = 24; + frs[0].new_connection_id.retire_prior_to = 17; + ngtcp2_cid_init(&frs[0].new_connection_id.cid, cid, sizeof(cid)); + frs[0].new_connection_id.cid.data[0] = (uint8_t)(i + 24); + memcpy(frs[0].new_connection_id.stateless_reset_token, token, sizeof(token)); + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(NGTCP2_ERR_CONNECTION_ID_LIMIT == rv); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_recv_retire_connection_id(void) { + ngtcp2_conn *conn; + uint8_t buf[2048]; + size_t pktlen; + ngtcp2_ssize spktlen; + ngtcp2_tstamp t = 1000000009; + int64_t pkt_num = 0; + ngtcp2_frame fr; + int rv; + ngtcp2_ksl_it it; + ngtcp2_scid *scid; + uint64_t seq; + + setup_default_client(&conn); + conn->remote.transport_params->active_connection_id_limit = 7; + + /* This will send NEW_CONNECTION_ID frames */ + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), t); + + CU_ASSERT(spktlen > 0); + + it = ngtcp2_ksl_begin(&conn->scid.set); + scid = ngtcp2_ksl_it_get(&it); + seq = scid->seq; + + CU_ASSERT(NGTCP2_SCID_FLAG_NONE == scid->flags); + CU_ASSERT(UINT64_MAX == scid->retired_ts); + CU_ASSERT(1 == ngtcp2_pq_size(&conn->scid.used)); + + fr.type = NGTCP2_FRAME_RETIRE_CONNECTION_ID; + fr.retire_connection_id.seq = seq; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(NGTCP2_SCID_FLAG_RETIRED == scid->flags); + CU_ASSERT(1000000010 == scid->retired_ts); + CU_ASSERT(2 == ngtcp2_pq_size(&conn->scid.used)); + CU_ASSERT(7 == ngtcp2_ksl_len(&conn->scid.set)); + CU_ASSERT(1 == conn->scid.num_retired); + + /* One NEW_CONNECTION_ID frame is sent as a replacement. */ + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(8 == ngtcp2_ksl_len(&conn->scid.set)); + CU_ASSERT(1 == conn->scid.num_retired); + + /* No NEW_CONNECTION_ID frames should be sent. */ + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen == 0); + CU_ASSERT(8 == ngtcp2_ksl_len(&conn->scid.set)); + CU_ASSERT(1 == conn->scid.num_retired); + + /* Now time passed and retired connection ID is removed */ + t += 7 * NGTCP2_DEFAULT_INITIAL_RTT; + + ngtcp2_conn_handle_expiry(conn, t); + + CU_ASSERT(7 == ngtcp2_ksl_len(&conn->scid.set)); + CU_ASSERT(0 == conn->scid.num_retired); + + ngtcp2_conn_del(conn); + + /* Receiving RETIRE_CONNECTION_ID with seq which is greater than the + sequence number previously sent must be treated as error */ + setup_default_server(&conn); + + fr.type = NGTCP2_FRAME_RETIRE_CONNECTION_ID; + fr.retire_connection_id.seq = 1; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(NGTCP2_ERR_PROTO == rv); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_server_path_validation(void) { + ngtcp2_conn *conn; + uint8_t buf[2048]; + size_t pktlen; + ngtcp2_ssize spktlen; + ngtcp2_tstamp t = 900; + int64_t pkt_num = 0; + ngtcp2_frame fr; + int rv; + const uint8_t raw_cid[] = {0x0f, 0x00, 0x00, 0x00}; + ngtcp2_cid cid, *new_cid; + const uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN] = {0xff}; + ngtcp2_path_storage new_path1, new_path2; + ngtcp2_ksl_it it; + + path_init(&new_path1, 0, 0, 2, 0); + path_init(&new_path2, 0, 0, 3, 0); + + ngtcp2_cid_init(&cid, raw_cid, sizeof(raw_cid)); + + setup_default_server(&conn); + + /* This will send NEW_CONNECTION_ID frames */ + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(ngtcp2_ksl_len(&conn->scid.set) > 1); + + fr.type = NGTCP2_FRAME_NEW_CONNECTION_ID; + fr.new_connection_id.seq = 1; + fr.new_connection_id.retire_prior_to = 0; + fr.new_connection_id.cid = cid; + memcpy(fr.new_connection_id.stateless_reset_token, token, sizeof(token)); + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + fr.type = NGTCP2_FRAME_PING; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &new_path1.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(NULL != conn->pv); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(ngtcp2_ringbuf_len(&conn->pv->ents.rb) > 0); + + fr.type = NGTCP2_FRAME_PATH_RESPONSE; + memset(fr.path_response.data, 0, sizeof(fr.path_response.data)); + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &new_path1.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(ngtcp2_path_eq(&new_path1.path, &conn->dcid.current.ps.path)); + /* DCID does not change because the client does not change its + DCID. */ + CU_ASSERT(!ngtcp2_cid_eq(&cid, &conn->dcid.current.cid)); + + /* A remote endpoint changes DCID as well */ + fr.type = NGTCP2_FRAME_PING; + + it = ngtcp2_ksl_begin(&conn->scid.set); + + assert(!ngtcp2_ksl_it_end(&it)); + + new_cid = &(((ngtcp2_scid *)ngtcp2_ksl_it_get(&it))->cid); + + pktlen = write_pkt(buf, sizeof(buf), new_cid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &new_path2.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(NULL != conn->pv); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(ngtcp2_ringbuf_len(&conn->pv->ents.rb) > 0); + + fr.type = NGTCP2_FRAME_PATH_RESPONSE; + memset(fr.path_response.data, 0, sizeof(fr.path_response.data)); + + pktlen = write_pkt(buf, sizeof(buf), new_cid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &new_path2.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(ngtcp2_path_eq(&new_path2.path, &conn->dcid.current.ps.path)); + CU_ASSERT(ngtcp2_cid_eq(&cid, &conn->dcid.current.cid)); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_client_connection_migration(void) { + ngtcp2_conn *conn; + uint8_t buf[2048]; + size_t pktlen; + ngtcp2_tstamp t = 900; + int64_t pkt_num = 0; + ngtcp2_frame fr; + int rv; + const uint8_t raw_cid[] = {0x0f, 0x00, 0x00, 0x00}; + ngtcp2_cid cid; + const uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN] = {0xff}; + my_user_data ud; + ngtcp2_ssize spktlen; + ngtcp2_path_storage to_path; + + ngtcp2_cid_init(&cid, raw_cid, sizeof(raw_cid)); + + /* immediate migration */ + setup_default_client(&conn); + + fr.type = NGTCP2_FRAME_NEW_CONNECTION_ID; + fr.new_connection_id.seq = 1; + fr.new_connection_id.retire_prior_to = 0; + fr.new_connection_id.cid = cid; + memcpy(fr.new_connection_id.stateless_reset_token, token, sizeof(token)); + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + ngtcp2_path_storage_init2(&to_path, &new_path.path); + to_path.path.user_data = &ud; + + rv = ngtcp2_conn_initiate_immediate_migration(conn, &to_path.path, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(NULL != conn->pv); + CU_ASSERT(ngtcp2_path_eq(&to_path.path, &conn->dcid.current.ps.path)); + CU_ASSERT(&ud == conn->dcid.current.ps.path.user_data); + CU_ASSERT(ngtcp2_cid_eq(&cid, &conn->dcid.current.cid)); + CU_ASSERT(ngtcp2_path_eq(&to_path.path, &conn->pv->dcid.ps.path)); + CU_ASSERT(&ud == conn->pv->dcid.ps.path.user_data); + CU_ASSERT(ngtcp2_cid_eq(&cid, &conn->pv->dcid.cid)); + + ngtcp2_conn_del(conn); + + /* migrate after successful path validation */ + setup_default_client(&conn); + + fr.type = NGTCP2_FRAME_NEW_CONNECTION_ID; + fr.new_connection_id.seq = 1; + fr.new_connection_id.retire_prior_to = 0; + fr.new_connection_id.cid = cid; + memcpy(fr.new_connection_id.stateless_reset_token, token, sizeof(token)); + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + ngtcp2_path_storage_init2(&to_path, &new_path.path); + to_path.path.user_data = &ud; + + rv = ngtcp2_conn_initiate_migration(conn, &to_path.path, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(NULL != conn->pv); + CU_ASSERT(ngtcp2_path_eq(&null_path.path, &conn->dcid.current.ps.path)); + CU_ASSERT(NULL == conn->dcid.current.ps.path.user_data); + CU_ASSERT(ngtcp2_cid_eq(&conn->rcid, &conn->dcid.current.cid)); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(0 < spktlen); + + fr.type = NGTCP2_FRAME_PATH_RESPONSE; + memset(fr.path_response.data, 0, sizeof(fr.path_response.data)); + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(NULL == conn->pv); + CU_ASSERT(ngtcp2_path_eq(&to_path.path, &conn->dcid.current.ps.path)); + CU_ASSERT(&ud == conn->dcid.current.ps.path.user_data); + CU_ASSERT(ngtcp2_cid_eq(&cid, &conn->dcid.current.cid)); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_recv_path_challenge(void) { + ngtcp2_conn *conn; + uint8_t buf[2048]; + size_t pktlen; + ngtcp2_ssize spktlen; + ngtcp2_tstamp t = 11; + int64_t pkt_num = 0; + ngtcp2_frame fr; + ngtcp2_frame frs[2]; + int rv; + const uint8_t raw_cid[] = {0x0f, 0x00, 0x00, 0x00}; + ngtcp2_cid cid; + const uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN] = {0xff}; + const uint8_t data[] = {0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8}; + const uint8_t data2[] = {0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf9}; + ngtcp2_path_storage ps; + ngtcp2_ssize shdlen; + ngtcp2_pkt_hd hd; + ngtcp2_dcid *dcid; + ngtcp2_settings settings; + ngtcp2_transport_params params; + + ngtcp2_cid_init(&cid, raw_cid, sizeof(raw_cid)); + + setup_default_server(&conn); + + fr.type = NGTCP2_FRAME_NEW_CONNECTION_ID; + fr.new_connection_id.seq = 1; + fr.new_connection_id.retire_prior_to = 0; + fr.new_connection_id.cid = cid; + memcpy(fr.new_connection_id.stateless_reset_token, token, sizeof(token)); + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + frs[0].type = NGTCP2_FRAME_PATH_CHALLENGE; + memcpy(frs[0].path_challenge.data, data, sizeof(frs[0].path_challenge.data)); + frs[1].type = NGTCP2_FRAME_PADDING; + frs[1].padding.len = 1200; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 2, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &new_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(ngtcp2_ringbuf_len(&conn->rx.path_challenge.rb) > 0); + + ngtcp2_path_storage_zero(&ps); + + spktlen = ngtcp2_conn_write_pkt(conn, &ps.path, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen >= 1200); + CU_ASSERT(ngtcp2_path_eq(&new_path.path, &ps.path)); + CU_ASSERT(0 == ngtcp2_ringbuf_len(&conn->rx.path_challenge.rb)); + CU_ASSERT(1 == ngtcp2_ringbuf_len(&conn->dcid.bound.rb)); + + dcid = ngtcp2_ringbuf_get(&conn->dcid.bound.rb, 0); + + CU_ASSERT((uint64_t)spktlen == dcid->bytes_sent); + + shdlen = ngtcp2_pkt_decode_hd_short(&hd, buf, (size_t)spktlen, cid.datalen); + + CU_ASSERT(shdlen > 0); + CU_ASSERT(ngtcp2_cid_eq(&cid, &hd.dcid)); + + /* Use same bound DCID for PATH_CHALLENGE from the same path. */ + fr.type = NGTCP2_FRAME_PATH_CHALLENGE; + memcpy(fr.path_challenge.data, data2, sizeof(fr.path_challenge.data)); + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &new_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(ngtcp2_ringbuf_len(&conn->rx.path_challenge.rb) > 0); + + ngtcp2_path_storage_zero(&ps); + + spktlen = ngtcp2_conn_write_pkt(conn, &ps.path, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(ngtcp2_path_eq(&new_path.path, &ps.path)); + CU_ASSERT(0 == ngtcp2_ringbuf_len(&conn->rx.path_challenge.rb)); + CU_ASSERT(1 == ngtcp2_ringbuf_len(&conn->dcid.bound.rb)); + + shdlen = ngtcp2_pkt_decode_hd_short(&hd, buf, (size_t)spktlen, cid.datalen); + + CU_ASSERT(shdlen > 0); + CU_ASSERT(ngtcp2_cid_eq(&cid, &hd.dcid)); + + ngtcp2_conn_del(conn); + + /* PATH_CHALLENGE from the current path */ + setup_default_server(&conn); + + fr.type = NGTCP2_FRAME_NEW_CONNECTION_ID; + fr.new_connection_id.seq = 1; + fr.new_connection_id.retire_prior_to = 0; + fr.new_connection_id.cid = cid; + memcpy(fr.new_connection_id.stateless_reset_token, token, sizeof(token)); + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + frs[0].type = NGTCP2_FRAME_PATH_CHALLENGE; + memcpy(frs[0].path_challenge.data, data, sizeof(frs[0].path_challenge.data)); + frs[1].type = NGTCP2_FRAME_PADDING; + frs[1].padding.len = 1200; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 2, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(ngtcp2_ringbuf_len(&conn->rx.path_challenge.rb) > 0); + + ngtcp2_path_storage_zero(&ps); + + spktlen = ngtcp2_conn_write_pkt(conn, &ps.path, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen >= 1200); + CU_ASSERT(ngtcp2_path_eq(&null_path.path, &ps.path)); + CU_ASSERT(0 == ngtcp2_ringbuf_len(&conn->rx.path_challenge.rb)); + CU_ASSERT(0 == ngtcp2_ringbuf_len(&conn->dcid.bound.rb)); + CU_ASSERT((uint64_t)spktlen == conn->dcid.current.bytes_sent); + + ngtcp2_conn_del(conn); + + /* PATH_CHALLENGE should be ignored with server + disable_active_migration */ + setup_default_server(&conn); + + conn->local.transport_params.disable_active_migration = 1; + + fr.type = NGTCP2_FRAME_NEW_CONNECTION_ID; + fr.new_connection_id.seq = 1; + fr.new_connection_id.retire_prior_to = 0; + fr.new_connection_id.cid = cid; + memcpy(fr.new_connection_id.stateless_reset_token, token, sizeof(token)); + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + frs[0].type = NGTCP2_FRAME_PATH_CHALLENGE; + memcpy(frs[0].path_challenge.data, data, sizeof(frs[0].path_challenge.data)); + frs[1].type = NGTCP2_FRAME_PADDING; + frs[1].padding.len = 1200; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 2, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &new_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(0 == ngtcp2_ringbuf_len(&conn->rx.path_challenge.rb)); + + ngtcp2_conn_del(conn); + + /* PATH_CHALLENGE on NAT rebinding (passive migration) should be + accepted with server disable_active_migration */ + setup_default_server(&conn); + + conn->local.transport_params.disable_active_migration = 1; + + fr.type = NGTCP2_FRAME_NEW_CONNECTION_ID; + fr.new_connection_id.seq = 1; + fr.new_connection_id.retire_prior_to = 0; + fr.new_connection_id.cid = cid; + memcpy(fr.new_connection_id.stateless_reset_token, token, sizeof(token)); + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + frs[0].type = NGTCP2_FRAME_PATH_CHALLENGE; + memcpy(frs[0].path_challenge.data, data, sizeof(frs[0].path_challenge.data)); + frs[1].type = NGTCP2_FRAME_PADDING; + frs[1].padding.len = 1200; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 2, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &new_nat_path.path, &null_pi, buf, pktlen, + ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(ngtcp2_ringbuf_len(&conn->rx.path_challenge.rb) > 0); + + ngtcp2_conn_del(conn); + + /* PATH_CHALLENGE to preferred address should be accepted with + server disable_active_migration */ + server_default_transport_params(¶ms); + params.disable_active_migration = 1; + params.preferred_address_present = 1; + params.preferred_address.cid = cid; + + /* Set local address of new_path */ + assert(AF_INET == new_path.path.local.addr->sa_family); + + params.preferred_address.ipv4_present = 1; + memcpy(¶ms.preferred_address.ipv4, new_path.path.local.addr, + sizeof(params.preferred_address.ipv4)); + + server_default_settings(&settings); + + setup_default_server_settings(&conn, &null_path.path, &settings, ¶ms); + + fr.type = NGTCP2_FRAME_NEW_CONNECTION_ID; + fr.new_connection_id.seq = 1; + fr.new_connection_id.retire_prior_to = 0; + fr.new_connection_id.cid = cid; + memcpy(fr.new_connection_id.stateless_reset_token, token, sizeof(token)); + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + frs[0].type = NGTCP2_FRAME_PATH_CHALLENGE; + memcpy(frs[0].path_challenge.data, data, sizeof(frs[0].path_challenge.data)); + frs[1].type = NGTCP2_FRAME_PADDING; + frs[1].padding.len = 1200; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 2, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &new_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(ngtcp2_ringbuf_len(&conn->rx.path_challenge.rb) > 0); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_key_update(void) { + ngtcp2_conn *conn; + uint8_t buf[2048]; + size_t pktlen; + ngtcp2_ssize spktlen; + ngtcp2_tstamp t = 19393; + int64_t pkt_num = -1; + ngtcp2_frame fr; + int rv; + int64_t stream_id; + ngtcp2_ssize nwrite; + + setup_default_server(&conn); + + /* The remote endpoint initiates key update */ + fr.type = NGTCP2_FRAME_PING; + + pktlen = + write_pkt_flags(buf, sizeof(buf), NGTCP2_PKT_FLAG_KEY_PHASE, &conn->oscid, + ++pkt_num, &fr, 1, conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(NULL != conn->crypto.key_update.old_rx_ckm); + CU_ASSERT(NULL == conn->crypto.key_update.new_tx_ckm); + CU_ASSERT(NULL == conn->crypto.key_update.new_rx_ckm); + CU_ASSERT(UINT64_MAX == conn->crypto.key_update.confirmed_ts); + CU_ASSERT(conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED); + CU_ASSERT(!(conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR)); + + t += NGTCP2_SECONDS; + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), t); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(t == conn->crypto.key_update.confirmed_ts); + CU_ASSERT(!(conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED)); + CU_ASSERT(!(conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR)); + + t += ngtcp2_conn_get_pto(conn) + 1; + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), t); + + CU_ASSERT(0 == spktlen); + CU_ASSERT(NULL == conn->crypto.key_update.old_rx_ckm); + CU_ASSERT(NULL != conn->crypto.key_update.new_tx_ckm); + CU_ASSERT(NULL != conn->crypto.key_update.new_rx_ckm); + + /* The local endpoint initiates key update */ + t += ngtcp2_conn_get_pto(conn) * 2; + + rv = ngtcp2_conn_initiate_key_update(conn, t); + + CU_ASSERT(0 == rv); + CU_ASSERT(NULL != conn->crypto.key_update.old_rx_ckm); + CU_ASSERT(NULL == conn->crypto.key_update.new_tx_ckm); + CU_ASSERT(NULL == conn->crypto.key_update.new_rx_ckm); + CU_ASSERT(conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED); + CU_ASSERT(conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR); + + rv = ngtcp2_conn_open_uni_stream(conn, &stream_id, NULL); + + CU_ASSERT(0 == rv); + + spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), + &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE, + stream_id, null_data, 1024, ++t); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED); + + fr.type = NGTCP2_FRAME_ACK; + fr.ack.largest_ack = conn->pktns.tx.last_pkt_num; + fr.ack.ack_delay = 0; + fr.ack.first_ack_range = 0; + fr.ack.rangecnt = 0; + + pktlen = + write_pkt_flags(buf, sizeof(buf), NGTCP2_PKT_FLAG_KEY_PHASE, &conn->oscid, + ++pkt_num, &fr, 1, conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(t == conn->crypto.key_update.confirmed_ts); + CU_ASSERT(!(conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_NOT_CONFIRMED)); + CU_ASSERT(!(conn->flags & NGTCP2_CONN_FLAG_KEY_UPDATE_INITIATOR)); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_crypto_buffer_exceeded(void) { + ngtcp2_conn *conn; + uint8_t buf[2048]; + size_t pktlen; + ngtcp2_tstamp t = 11111; + int64_t pkt_num = -1; + ngtcp2_frame fr; + int rv; + + setup_default_client(&conn); + + fr.type = NGTCP2_FRAME_CRYPTO; + fr.crypto.offset = 1000000; + fr.crypto.datacnt = 1; + fr.crypto.data[0].base = null_data; + fr.crypto.data[0].len = 1; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(NGTCP2_ERR_CRYPTO_BUFFER_EXCEEDED == rv); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_handshake_probe(void) { + ngtcp2_conn *conn; + ngtcp2_tstamp t = 0; + ngtcp2_ssize spktlen; + size_t pktlen; + uint8_t buf[1200]; + ngtcp2_frame fr; + ngtcp2_rtb_entry *ent; + ngtcp2_ksl_it it; + int rv; + ngtcp2_crypto_aead_ctx aead_ctx = {0}; + ngtcp2_crypto_cipher_ctx hp_ctx = {0}; + ngtcp2_crypto_ctx crypto_ctx; + + /* Retransmit first Initial on PTO timer */ + setup_handshake_client(&conn); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(1 == conn->in_pktns->rtb.num_ack_eliciting); + + rv = ngtcp2_conn_on_loss_detection_timer(conn, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(1 == conn->in_pktns->rtb.probe_pkt_left); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(1 == conn->in_pktns->rtb.num_retransmittable); + CU_ASSERT(2 == conn->in_pktns->rtb.num_ack_eliciting); + CU_ASSERT(0 == conn->in_pktns->rtb.probe_pkt_left); + + fr.type = NGTCP2_FRAME_ACK; + fr.ack.largest_ack = 0; + fr.ack.ack_delay = 0; + fr.ack.first_ack_range = 0; + fr.ack.rangecnt = 0; + + pktlen = write_initial_pkt(buf, sizeof(buf), &conn->oscid, + ngtcp2_conn_get_dcid(conn), 0, NGTCP2_PROTO_VER_V1, + NULL, 0, &fr, 1, &null_ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(1 == conn->in_pktns->rtb.num_ack_eliciting); + + rv = ngtcp2_conn_on_loss_detection_timer(conn, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(1 == conn->in_pktns->rtb.num_retransmittable); + CU_ASSERT(1 == conn->in_pktns->rtb.num_ack_eliciting); + CU_ASSERT(1 == conn->in_pktns->rtb.probe_pkt_left); + + /* This sends anti-deadlock padded Initial packet even if we have + nothing to send. */ + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(0 == conn->in_pktns->rtb.num_retransmittable); + CU_ASSERT(2 == conn->in_pktns->rtb.num_ack_eliciting); + CU_ASSERT(0 == conn->in_pktns->rtb.probe_pkt_left); + + it = ngtcp2_rtb_head(&conn->in_pktns->rtb); + ent = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(ent->flags & NGTCP2_RTB_ENTRY_FLAG_PROBE); + CU_ASSERT(sizeof(buf) == ent->pktlen); + + init_crypto_ctx(&crypto_ctx); + ngtcp2_conn_set_crypto_ctx(conn, &crypto_ctx); + conn->negotiated_version = conn->client_chosen_version; + ngtcp2_conn_install_rx_handshake_key(conn, &aead_ctx, null_iv, + sizeof(null_iv), &hp_ctx); + ngtcp2_conn_install_tx_handshake_key(conn, &aead_ctx, null_iv, + sizeof(null_iv), &hp_ctx); + + rv = ngtcp2_conn_on_loss_detection_timer(conn, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(2 == conn->in_pktns->rtb.num_ack_eliciting); + CU_ASSERT(1 == conn->hs_pktns->rtb.probe_pkt_left); + + /* This sends anti-deadlock Handshake packet even if we have nothing + to send. */ + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(0 == conn->hs_pktns->rtb.num_retransmittable); + CU_ASSERT(1 == conn->hs_pktns->rtb.num_ack_eliciting); + CU_ASSERT(0 == conn->hs_pktns->rtb.probe_pkt_left); + + it = ngtcp2_rtb_head(&conn->hs_pktns->rtb); + ent = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(ent->flags & NGTCP2_RTB_ENTRY_FLAG_PROBE); + CU_ASSERT(sizeof(buf) > ent->pktlen); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_handshake_loss(void) { + ngtcp2_conn *conn; + ngtcp2_tstamp t = 0; + ngtcp2_ssize spktlen; + size_t i; + size_t pktlen; + uint8_t buf[1252]; + ngtcp2_frame fr; + ngtcp2_frame frs[2]; + ngtcp2_cid rcid; + int rv; + int64_t pkt_num = -1; + ngtcp2_ksl_it it; + ngtcp2_rtb_entry *ent; + + rcid_init(&rcid); + setup_handshake_server(&conn); + conn->callbacks.recv_crypto_data = recv_crypto_data; + + frs[0].type = NGTCP2_FRAME_CRYPTO; + frs[0].crypto.offset = 0; + frs[0].crypto.datacnt = 1; + frs[0].crypto.data[0].len = 123; + frs[0].crypto.data[0].base = null_data; + + frs[1].type = NGTCP2_FRAME_PADDING; + frs[1].padding.len = 1005; + + pktlen = write_initial_pkt( + buf, sizeof(buf), &rcid, ngtcp2_conn_get_dcid(conn), ++pkt_num, + conn->client_chosen_version, NULL, 0, frs, 2, &null_ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + /* Increase anti-amplification factor for easier testing */ + conn->dcid.current.bytes_recv += 10000; + + ngtcp2_conn_submit_crypto_data(conn, NGTCP2_CRYPTO_LEVEL_INITIAL, null_data, + 123); + ngtcp2_conn_submit_crypto_data(conn, NGTCP2_CRYPTO_LEVEL_HANDSHAKE, null_data, + 163); + ngtcp2_conn_submit_crypto_data(conn, NGTCP2_CRYPTO_LEVEL_HANDSHAKE, null_data, + 2369); + ngtcp2_conn_submit_crypto_data(conn, NGTCP2_CRYPTO_LEVEL_HANDSHAKE, null_data, + 79); + ngtcp2_conn_submit_crypto_data(conn, NGTCP2_CRYPTO_LEVEL_HANDSHAKE, null_data, + 36); + + /* Initial and first Handshake are coalesced into 1 packet. */ + for (i = 0; i < 3; ++i) { + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + CU_ASSERT(spktlen > 0); + } + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(0 == spktlen); + + t += 30 * NGTCP2_MILLISECONDS; + + ngtcp2_conn_on_loss_detection_timer(conn, t); + + CU_ASSERT(1 == conn->in_pktns->rtb.probe_pkt_left); + CU_ASSERT(1 == conn->hs_pktns->rtb.probe_pkt_left); + + /* Send a PTO probe packet */ + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(0 == spktlen); + + it = ngtcp2_ksl_begin(&conn->hs_pktns->rtb.ents); + ent = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(0 == ent->frc->fr.crypto.offset); + CU_ASSERT(987 == ngtcp2_vec_len(ent->frc->fr.crypto.data, + ent->frc->fr.crypto.datacnt)); + CU_ASSERT(3 == ent->hd.pkt_num); + + fr.type = NGTCP2_FRAME_ACK; + fr.ack.largest_ack = 2; + fr.ack.ack_delay = 0; + fr.ack.ack_delay_unscaled = 0; + fr.ack.first_ack_range = 0; + fr.ack.rangecnt = 0; + + pktlen = write_handshake_pkt(buf, sizeof(buf), &conn->oscid, + ngtcp2_conn_get_dcid(conn), ++pkt_num, + conn->client_chosen_version, &fr, 1, &null_ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, t); + + CU_ASSERT(0 == rv); + + t += 40 * NGTCP2_MILLISECONDS; + + ngtcp2_conn_on_loss_detection_timer(conn, t); + + CU_ASSERT(0 == conn->hs_pktns->rtb.probe_pkt_left); + + /* Retransmits the contents of lost packet */ + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + + it = ngtcp2_ksl_begin(&conn->hs_pktns->rtb.ents); + ent = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(NGTCP2_FRAME_CRYPTO == ent->frc->fr.type); + CU_ASSERT(987 == ent->frc->fr.crypto.offset); + CU_ASSERT(1 == ent->frc->fr.crypto.datacnt); + CU_ASSERT(1183 == ngtcp2_vec_len(ent->frc->fr.crypto.data, + ent->frc->fr.crypto.datacnt)); + CU_ASSERT(4 == ent->hd.pkt_num); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(0 == spktlen); + + t += 30 * NGTCP2_MILLISECONDS; + + ngtcp2_conn_on_loss_detection_timer(conn, t); + + CU_ASSERT(1 == conn->hs_pktns->rtb.probe_pkt_left); + + /* Send a PTO probe packet */ + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + + it = ngtcp2_ksl_begin(&conn->hs_pktns->rtb.ents); + ent = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(NGTCP2_FRAME_CRYPTO == ent->frc->fr.type); + CU_ASSERT(0 == ent->frc->fr.crypto.offset); + CU_ASSERT(2 == ent->frc->fr.crypto.datacnt); + CU_ASSERT(987 == ngtcp2_vec_len(ent->frc->fr.crypto.data, + ent->frc->fr.crypto.datacnt)); + CU_ASSERT(5 == ent->hd.pkt_num); + + ngtcp2_conn_del(conn); + + /* Retransmission splits CRYPTO frame */ + setup_handshake_server(&conn); + conn->callbacks.recv_crypto_data = recv_crypto_data; + + frs[0].type = NGTCP2_FRAME_CRYPTO; + frs[0].crypto.offset = 0; + frs[0].crypto.datacnt = 1; + frs[0].crypto.data[0].len = 123; + frs[0].crypto.data[0].base = null_data; + + frs[1].type = NGTCP2_FRAME_PADDING; + frs[1].padding.len = 1005; + + pktlen = write_initial_pkt( + buf, sizeof(buf), &rcid, ngtcp2_conn_get_dcid(conn), ++pkt_num, + conn->client_chosen_version, NULL, 0, frs, 2, &null_ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + /* Increase anti-amplification factor for easier testing */ + conn->dcid.current.bytes_recv += 10000; + + ngtcp2_conn_submit_crypto_data(conn, NGTCP2_CRYPTO_LEVEL_INITIAL, null_data, + 123); + ngtcp2_conn_submit_crypto_data(conn, NGTCP2_CRYPTO_LEVEL_HANDSHAKE, null_data, + 3000); + /* Initial and first Handshake are coalesced into 1 packet. */ + for (i = 0; i < 3; ++i) { + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + CU_ASSERT(spktlen > 0); + } + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(0 == spktlen); + + it = ngtcp2_ksl_begin(&conn->hs_pktns->rtb.ents); + ent = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(NGTCP2_FRAME_CRYPTO == ent->frc->fr.type); + CU_ASSERT(2170 == ent->frc->fr.crypto.offset); + CU_ASSERT(830 == ngtcp2_vec_len(ent->frc->fr.crypto.data, + ent->frc->fr.crypto.datacnt)); + CU_ASSERT(2 == ent->hd.pkt_num); + + t += 30 * NGTCP2_MILLISECONDS; + + ngtcp2_conn_on_loss_detection_timer(conn, t); + + CU_ASSERT(1 == conn->in_pktns->rtb.probe_pkt_left); + CU_ASSERT(1 == conn->hs_pktns->rtb.probe_pkt_left); + + /* 1st PTO */ + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + CU_ASSERT(spktlen > 0); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(0 == spktlen); + + it = ngtcp2_ksl_begin(&conn->hs_pktns->rtb.ents); + ent = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(NGTCP2_FRAME_CRYPTO == ent->frc->fr.type); + CU_ASSERT(0 == ent->frc->fr.crypto.offset); + CU_ASSERT(987 == ngtcp2_vec_len(ent->frc->fr.crypto.data, + ent->frc->fr.crypto.datacnt)); + CU_ASSERT(3 == ent->hd.pkt_num); + + t += 30 * NGTCP2_MILLISECONDS; + + ngtcp2_conn_on_loss_detection_timer(conn, t); + + CU_ASSERT(1 == conn->in_pktns->rtb.probe_pkt_left); + CU_ASSERT(1 == conn->hs_pktns->rtb.probe_pkt_left); + + /* 2nd PTO. Initial and Handshake packets are coalesced. Handshake + CRYPTO is split into 2 because of Initial CRYPTO. */ + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + CU_ASSERT(spktlen > 0); + + it = ngtcp2_ksl_begin(&conn->hs_pktns->rtb.ents); + ent = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(NGTCP2_FRAME_CRYPTO == ent->frc->fr.type); + CU_ASSERT(987 == ent->frc->fr.crypto.offset); + CU_ASSERT(991 == ngtcp2_vec_len(ent->frc->fr.crypto.data, + ent->frc->fr.crypto.datacnt)); + CU_ASSERT(4 == ent->hd.pkt_num); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + CU_ASSERT(spktlen > 0); + + it = ngtcp2_ksl_begin(&conn->hs_pktns->rtb.ents); + ent = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(NGTCP2_FRAME_CRYPTO == ent->frc->fr.type); + CU_ASSERT(1978 == ent->frc->fr.crypto.offset); + CU_ASSERT(192 == ngtcp2_vec_len(ent->frc->fr.crypto.data, + ent->frc->fr.crypto.datacnt)); + CU_ASSERT(5 == ent->hd.pkt_num); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + CU_ASSERT(0 == spktlen); + + fr.type = NGTCP2_FRAME_ACK; + fr.ack.largest_ack = 0; + fr.ack.ack_delay = 0; + fr.ack.ack_delay_unscaled = 0; + fr.ack.first_ack_range = 0; + fr.ack.rangecnt = 0; + + pktlen = write_handshake_pkt(buf, sizeof(buf), &conn->oscid, + ngtcp2_conn_get_dcid(conn), ++pkt_num, + conn->client_chosen_version, &fr, 1, &null_ckm); + + t += NGTCP2_MILLISECONDS; + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, t); + + CU_ASSERT(0 == rv); + + t += 40 * NGTCP2_MILLISECONDS; + + ngtcp2_conn_on_loss_detection_timer(conn, t); + + CU_ASSERT(1 == conn->hs_pktns->rtb.probe_pkt_left); + + /* 3rd PTO */ + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + CU_ASSERT(0 == spktlen); + + it = ngtcp2_ksl_begin(&conn->hs_pktns->rtb.ents); + ent = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(NGTCP2_FRAME_CRYPTO == ent->frc->fr.type); + CU_ASSERT(2170 == ent->frc->fr.crypto.offset); + CU_ASSERT(830 == ngtcp2_vec_len(ent->frc->fr.crypto.data, + ent->frc->fr.crypto.datacnt)); + CU_ASSERT(6 == ent->hd.pkt_num); + CU_ASSERT(NULL == ent->frc->next); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_recv_client_initial_retry(void) { + ngtcp2_conn *conn; + uint8_t buf[2048]; + size_t pktlen; + ngtcp2_frame fr; + int64_t pkt_num = -1; + ngtcp2_tstamp t = 0; + ngtcp2_cid rcid; + int rv; + + rcid_init(&rcid); + + setup_handshake_server(&conn); + fr.type = NGTCP2_FRAME_CRYPTO; + fr.crypto.offset = 1; + fr.crypto.datacnt = 1; + fr.crypto.data[0].len = 1245; + fr.crypto.data[0].base = null_data; + + pktlen = write_initial_pkt( + buf, sizeof(buf), &rcid, ngtcp2_conn_get_dcid(conn), ++pkt_num, + conn->client_chosen_version, NULL, 0, &fr, 1, &null_ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(NGTCP2_ERR_RETRY == rv); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_recv_client_initial_token(void) { + ngtcp2_conn *conn; + uint8_t buf[2048]; + size_t pktlen; + ngtcp2_frame fr; + int64_t pkt_num = -1; + ngtcp2_tstamp t = 0; + ngtcp2_cid rcid; + int rv; + const uint8_t raw_token[] = {0xff, 0x12, 0x31, 0x04, 0xab}; + ngtcp2_vec token; + const ngtcp2_mem *mem; + + rcid_init(&rcid); + + setup_handshake_server(&conn); + mem = conn->mem; + + token.base = ngtcp2_mem_malloc(mem, sizeof(raw_token)); + memcpy(token.base, raw_token, sizeof(raw_token)); + token.len = sizeof(raw_token); + + conn->local.settings.token = token; + + fr.type = NGTCP2_FRAME_CRYPTO; + fr.crypto.offset = 0; + fr.crypto.datacnt = 1; + fr.crypto.data[0].len = 1181; + fr.crypto.data[0].base = null_data; + + pktlen = + write_initial_pkt(buf, sizeof(buf), &rcid, ngtcp2_conn_get_dcid(conn), + ++pkt_num, conn->client_chosen_version, raw_token, + sizeof(raw_token), &fr, 1, &null_ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(1181 == ngtcp2_strm_rx_offset(&conn->in_pktns->crypto.strm)); + + ngtcp2_conn_del(conn); + + /* Specifying invalid token lets server drop the packet */ + setup_handshake_server(&conn); + mem = conn->mem; + + token.base = ngtcp2_mem_malloc(mem, sizeof(raw_token)); + memcpy(token.base, raw_token, sizeof(raw_token)); + token.len = sizeof(raw_token) - 1; + + conn->local.settings.token = token; + + fr.type = NGTCP2_FRAME_CRYPTO; + fr.crypto.offset = 0; + fr.crypto.datacnt = 1; + fr.crypto.data[0].len = 1179; + fr.crypto.data[0].base = null_data; + + pktlen = + write_initial_pkt(buf, sizeof(buf), &rcid, ngtcp2_conn_get_dcid(conn), + ++pkt_num, conn->client_chosen_version, raw_token, + sizeof(raw_token), &fr, 1, &null_ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(NGTCP2_ERR_DROP_CONN == rv); + CU_ASSERT(0 == ngtcp2_strm_rx_offset(&conn->in_pktns->crypto.strm)); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_get_active_dcid(void) { + ngtcp2_conn *conn; + ngtcp2_cid_token cid_token[2]; + ngtcp2_cid dcid; + static uint8_t token[] = {0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, + 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1, 0xf1}; + + dcid_init(&dcid); + setup_default_client(&conn); + + CU_ASSERT(1 == ngtcp2_conn_get_num_active_dcid(conn)); + CU_ASSERT(1 == ngtcp2_conn_get_active_dcid(conn, cid_token)); + CU_ASSERT(0 == cid_token[0].seq); + CU_ASSERT(ngtcp2_cid_eq(&dcid, &cid_token[0].cid)); + CU_ASSERT(ngtcp2_path_eq(&null_path.path, &cid_token[0].ps.path)); + CU_ASSERT(1 == cid_token[0].token_present); + CU_ASSERT(0 == + memcmp(token, cid_token[0].token, NGTCP2_STATELESS_RESET_TOKENLEN)); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_recv_version_negotiation(void) { + ngtcp2_conn *conn; + const ngtcp2_cid *dcid; + ngtcp2_ssize spktlen; + uint8_t buf[1500]; + uint32_t nsv[3]; + int rv; + ngtcp2_tstamp t = 0; + + setup_handshake_client(&conn); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + + dcid = ngtcp2_conn_get_dcid(conn); + + nsv[0] = 0xffffffff; + + spktlen = ngtcp2_pkt_write_version_negotiation( + buf, sizeof(buf), 0xfe, conn->oscid.data, conn->oscid.datalen, dcid->data, + dcid->datalen, nsv, 1); + + CU_ASSERT(spktlen > 0); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, + (size_t)spktlen, ++t); + + CU_ASSERT(NGTCP2_ERR_RECV_VERSION_NEGOTIATION == rv); + + ngtcp2_conn_del(conn); + + /* Ignore Version Negotiation if it contains version selected by + client */ + setup_handshake_client(&conn); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + + dcid = ngtcp2_conn_get_dcid(conn); + + nsv[0] = 0xfffffff0; + nsv[1] = conn->client_chosen_version; + + spktlen = ngtcp2_pkt_write_version_negotiation( + buf, sizeof(buf), 0x50, conn->oscid.data, conn->oscid.datalen, dcid->data, + dcid->datalen, nsv, 2); + + CU_ASSERT(spktlen > 0); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, + (size_t)spktlen, ++t); + + CU_ASSERT(0 == rv); + + ngtcp2_conn_del(conn); + + /* Ignore Version Negotiation if client reacted upon Version + Negotiation */ + setup_handshake_client(&conn); + + conn->local.settings.original_version = NGTCP2_PROTO_VER_V2_DRAFT; + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + + dcid = ngtcp2_conn_get_dcid(conn); + + nsv[0] = 0xffffffff; + + spktlen = ngtcp2_pkt_write_version_negotiation( + buf, sizeof(buf), 0xfe, conn->oscid.data, conn->oscid.datalen, dcid->data, + dcid->datalen, nsv, 1); + + CU_ASSERT(spktlen > 0); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, + (size_t)spktlen, ++t); + + CU_ASSERT(0 == rv); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_send_initial_token(void) { + ngtcp2_conn *conn; + uint8_t buf[2048]; + ngtcp2_callbacks cb; + ngtcp2_settings settings; + ngtcp2_transport_params params; + ngtcp2_cid rcid, scid; + ngtcp2_crypto_aead retry_aead = {0, NGTCP2_FAKE_AEAD_OVERHEAD}; + uint8_t token[] = "this is token"; + ngtcp2_ssize spktlen, shdlen; + ngtcp2_tstamp t = 0; + ngtcp2_pkt_hd hd; + ngtcp2_crypto_aead_ctx aead_ctx = {0}; + ngtcp2_crypto_cipher_ctx hp_ctx = {0}; + ngtcp2_crypto_ctx crypto_ctx; + + rcid_init(&rcid); + scid_init(&scid); + + init_initial_crypto_ctx(&crypto_ctx); + + client_default_callbacks(&cb); + client_default_settings(&settings); + client_default_transport_params(¶ms); + + settings.token.base = token; + settings.token.len = sizeof(token); + + ngtcp2_conn_client_new(&conn, &rcid, &scid, &null_path.path, + NGTCP2_PROTO_VER_V1, &cb, &settings, ¶ms, + /* mem = */ NULL, NULL); + ngtcp2_conn_set_initial_crypto_ctx(conn, &crypto_ctx); + ngtcp2_conn_install_initial_key(conn, &aead_ctx, null_iv, &hp_ctx, &aead_ctx, + null_iv, &hp_ctx, sizeof(null_iv)); + ngtcp2_conn_set_retry_aead(conn, &retry_aead, &aead_ctx); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + + shdlen = ngtcp2_pkt_decode_hd_long(&hd, buf, (size_t)spktlen); + + CU_ASSERT(shdlen > 0); + CU_ASSERT(sizeof(token) == hd.token.len); + CU_ASSERT(0 == memcmp(token, hd.token.base, sizeof(token))); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_set_remote_transport_params(void) { + ngtcp2_conn *conn; + ngtcp2_transport_params params; + int rv; + ngtcp2_cid dcid; + uint8_t other_versions[2 * sizeof(uint32_t)]; + + dcid_init(&dcid); + + /* client: Successful case */ + setup_handshake_client(&conn); + + conn->negotiated_version = conn->client_chosen_version; + + memset(¶ms, 0, sizeof(params)); + params.active_connection_id_limit = NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT; + params.max_udp_payload_size = 1450; + params.initial_scid = conn->dcid.current.cid; + params.original_dcid = conn->rcid; + + rv = ngtcp2_conn_set_remote_transport_params(conn, ¶ms); + + CU_ASSERT(0 == rv); + + ngtcp2_conn_del(conn); + + /* client: Wrong original_dcid */ + setup_handshake_client(&conn); + + conn->negotiated_version = conn->client_chosen_version; + + memset(¶ms, 0, sizeof(params)); + params.active_connection_id_limit = NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT; + params.max_udp_payload_size = 1450; + params.initial_scid = conn->dcid.current.cid; + + rv = ngtcp2_conn_set_remote_transport_params(conn, ¶ms); + + CU_ASSERT(NGTCP2_ERR_TRANSPORT_PARAM == rv); + + ngtcp2_conn_del(conn); + + /* client: Wrong initial_scid */ + setup_handshake_client(&conn); + + conn->negotiated_version = conn->client_chosen_version; + + memset(¶ms, 0, sizeof(params)); + params.active_connection_id_limit = NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT; + params.max_udp_payload_size = 1450; + params.original_dcid = conn->rcid; + + rv = ngtcp2_conn_set_remote_transport_params(conn, ¶ms); + + CU_ASSERT(NGTCP2_ERR_TRANSPORT_PARAM == rv); + + ngtcp2_conn_del(conn); + + /* client: Receiving retry_scid when retry is not attempted */ + setup_handshake_client(&conn); + + conn->negotiated_version = conn->client_chosen_version; + + memset(¶ms, 0, sizeof(params)); + params.active_connection_id_limit = NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT; + params.max_udp_payload_size = 1450; + params.initial_scid = conn->dcid.current.cid; + params.original_dcid = conn->rcid; + params.retry_scid_present = 1; + + rv = ngtcp2_conn_set_remote_transport_params(conn, ¶ms); + + CU_ASSERT(NGTCP2_ERR_TRANSPORT_PARAM == rv); + + ngtcp2_conn_del(conn); + + /* client: Receiving retry_scid */ + setup_handshake_client(&conn); + + conn->flags |= NGTCP2_CONN_FLAG_RECV_RETRY; + conn->retry_scid = dcid; + conn->negotiated_version = conn->client_chosen_version; + + memset(¶ms, 0, sizeof(params)); + params.active_connection_id_limit = NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT; + params.max_udp_payload_size = 1450; + params.initial_scid = conn->dcid.current.cid; + params.original_dcid = conn->rcid; + params.retry_scid_present = 1; + params.retry_scid = dcid; + + rv = ngtcp2_conn_set_remote_transport_params(conn, ¶ms); + + CU_ASSERT(0 == rv); + + ngtcp2_conn_del(conn); + + /* client: Not receiving retry_scid when retry is attempted */ + setup_handshake_client(&conn); + + conn->flags |= NGTCP2_CONN_FLAG_RECV_RETRY; + conn->retry_scid = dcid; + conn->negotiated_version = conn->client_chosen_version; + + memset(¶ms, 0, sizeof(params)); + params.active_connection_id_limit = NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT; + params.max_udp_payload_size = 1450; + params.initial_scid = conn->dcid.current.cid; + params.original_dcid = conn->rcid; + + rv = ngtcp2_conn_set_remote_transport_params(conn, ¶ms); + + CU_ASSERT(NGTCP2_ERR_TRANSPORT_PARAM == rv); + + ngtcp2_conn_del(conn); + + /* client: Special handling for QUIC v1 regarding Version + Negotiation */ + setup_handshake_client(&conn); + + conn->local.settings.original_version = NGTCP2_PROTO_VER_V2_DRAFT; + conn->negotiated_version = conn->client_chosen_version; + + memset(¶ms, 0, sizeof(params)); + params.active_connection_id_limit = NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT; + params.max_udp_payload_size = 1450; + params.initial_scid = conn->dcid.current.cid; + params.original_dcid = conn->rcid; + + rv = ngtcp2_conn_set_remote_transport_params(conn, ¶ms); + + CU_ASSERT(0 == rv); + + ngtcp2_conn_del(conn); + + /* client: No version_information after Version Negotiation */ + setup_handshake_client_version(&conn, NGTCP2_PROTO_VER_V2_DRAFT); + + conn->local.settings.original_version = NGTCP2_PROTO_VER_V1; + conn->negotiated_version = conn->client_chosen_version; + + memset(¶ms, 0, sizeof(params)); + params.active_connection_id_limit = NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT; + params.max_udp_payload_size = 1450; + params.initial_scid = conn->dcid.current.cid; + params.original_dcid = conn->rcid; + + rv = ngtcp2_conn_set_remote_transport_params(conn, ¶ms); + + CU_ASSERT(NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE == rv); + + ngtcp2_conn_del(conn); + + /* client: other_versions includes the version that the client + initially attempted. */ + setup_handshake_client(&conn); + + conn->local.settings.original_version = NGTCP2_PROTO_VER_V2_DRAFT; + conn->negotiated_version = conn->client_chosen_version; + + ngtcp2_put_uint32be(other_versions, NGTCP2_PROTO_VER_V1); + ngtcp2_put_uint32be(other_versions + sizeof(uint32_t), + NGTCP2_PROTO_VER_V2_DRAFT); + + memset(¶ms, 0, sizeof(params)); + params.active_connection_id_limit = NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT; + params.max_udp_payload_size = 1450; + params.initial_scid = conn->dcid.current.cid; + params.original_dcid = conn->rcid; + params.version_info_present = 1; + params.version_info.chosen_version = conn->negotiated_version; + params.version_info.other_versions = other_versions; + params.version_info.other_versionslen = 2 * sizeof(uint32_t); + + rv = ngtcp2_conn_set_remote_transport_params(conn, ¶ms); + + CU_ASSERT(NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE == rv); + + ngtcp2_conn_del(conn); + + /* client: client is unable to choose client chosen version from + server's other_versions and chosen version. */ + setup_handshake_client(&conn); + + conn->local.settings.original_version = NGTCP2_PROTO_VER_V2_DRAFT; + conn->negotiated_version = 0xff000000u; + + conn->local.settings.preferred_versions[0] = 0xff000001u; + conn->local.settings.preferred_versionslen = 1; + + ngtcp2_put_uint32be(conn->vneg.other_versions, NGTCP2_PROTO_VER_V1); + ngtcp2_put_uint32be(conn->vneg.other_versions + sizeof(uint32_t), + 0xff000000u); + + ngtcp2_put_uint32be(other_versions, 0xff000000u); + + memset(¶ms, 0, sizeof(params)); + params.active_connection_id_limit = NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT; + params.max_udp_payload_size = 1450; + params.initial_scid = conn->dcid.current.cid; + params.original_dcid = conn->rcid; + params.version_info_present = 1; + params.version_info.chosen_version = conn->negotiated_version; + params.version_info.other_versions = other_versions; + params.version_info.other_versionslen = 1; + + rv = ngtcp2_conn_set_remote_transport_params(conn, ¶ms); + + CU_ASSERT(NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE == rv); + + ngtcp2_conn_del(conn); + + /* client: client chooses version which differs from client chosen + version from server's other_versions and chosen version. */ + setup_handshake_client(&conn); + + conn->local.settings.original_version = NGTCP2_PROTO_VER_V2_DRAFT; + conn->negotiated_version = 0xff000000u; + + conn->local.settings.preferred_versions[0] = 0xff000000u; + conn->local.settings.preferred_versionslen = 1; + + ngtcp2_put_uint32be(conn->vneg.other_versions, NGTCP2_PROTO_VER_V1); + ngtcp2_put_uint32be(conn->vneg.other_versions + sizeof(uint32_t), + 0xff000000u); + + ngtcp2_put_uint32be(other_versions, 0xff000000u); + + memset(¶ms, 0, sizeof(params)); + params.active_connection_id_limit = NGTCP2_DEFAULT_ACTIVE_CONNECTION_ID_LIMIT; + params.max_udp_payload_size = 1450; + params.initial_scid = conn->dcid.current.cid; + params.original_dcid = conn->rcid; + params.version_info_present = 1; + params.version_info.chosen_version = conn->negotiated_version; + params.version_info.other_versions = other_versions; + params.version_info.other_versionslen = 1; + + rv = ngtcp2_conn_set_remote_transport_params(conn, ¶ms); + + CU_ASSERT(NGTCP2_ERR_VERSION_NEGOTIATION_FAILURE == rv); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_write_connection_close(void) { + ngtcp2_conn *conn; + uint8_t buf[1200]; + ngtcp2_ssize spktlen, shdlen; + ngtcp2_pkt_hd hd; + const uint8_t *p; + ngtcp2_crypto_aead_ctx aead_ctx = {0}; + ngtcp2_crypto_cipher_ctx hp_ctx = {0}; + ngtcp2_crypto_ctx crypto_ctx; + ngtcp2_connection_close_error ccerr; + + /* Client only Initial key */ + setup_handshake_client(&conn); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), 0); + + CU_ASSERT(spktlen > 0); + + ngtcp2_connection_close_error_set_transport_error(&ccerr, NGTCP2_NO_ERROR, + (const uint8_t *)"foo", 3); + + spktlen = ngtcp2_conn_write_connection_close(conn, NULL, NULL, buf, + sizeof(buf), &ccerr, 0); + + CU_ASSERT(spktlen >= NGTCP2_MAX_UDP_PAYLOAD_SIZE); + + shdlen = ngtcp2_pkt_decode_hd_long(&hd, buf, (size_t)spktlen); + + CU_ASSERT(shdlen > 0); + CU_ASSERT(NGTCP2_PKT_INITIAL == hd.type); + CU_ASSERT(shdlen + (ngtcp2_ssize)hd.len == spktlen); + + ngtcp2_conn_del(conn); + + /* Client has Initial and Handshake keys */ + setup_handshake_client(&conn); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), 0); + + CU_ASSERT(spktlen > 0); + + init_crypto_ctx(&crypto_ctx); + + ngtcp2_conn_set_crypto_ctx(conn, &crypto_ctx); + conn->negotiated_version = conn->client_chosen_version; + ngtcp2_conn_install_tx_handshake_key(conn, &aead_ctx, null_iv, + sizeof(null_iv), &hp_ctx); + + ngtcp2_connection_close_error_set_transport_error_liberr(&ccerr, 0, NULL, 0); + + spktlen = ngtcp2_conn_write_connection_close(conn, NULL, NULL, buf, + sizeof(buf), &ccerr, 0); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(spktlen < NGTCP2_MAX_UDP_PAYLOAD_SIZE); + + shdlen = ngtcp2_pkt_decode_hd_long(&hd, buf, (size_t)spktlen); + + CU_ASSERT(shdlen > 0); + CU_ASSERT(NGTCP2_PKT_HANDSHAKE == hd.type); + CU_ASSERT(shdlen + (ngtcp2_ssize)hd.len == spktlen); + + ngtcp2_conn_del(conn); + + /* Client has all keys and has not confirmed handshake */ + setup_handshake_client(&conn); + + init_crypto_ctx(&crypto_ctx); + + ngtcp2_conn_set_crypto_ctx(conn, &crypto_ctx); + conn->negotiated_version = conn->client_chosen_version; + ngtcp2_conn_install_tx_handshake_key(conn, &aead_ctx, null_iv, + sizeof(null_iv), &hp_ctx); + ngtcp2_conn_install_tx_key(conn, null_secret, sizeof(null_secret), &aead_ctx, + null_iv, sizeof(null_iv), &hp_ctx); + + conn->state = NGTCP2_CS_POST_HANDSHAKE; + + ngtcp2_connection_close_error_set_transport_error(&ccerr, NGTCP2_NO_ERROR, + NULL, 0); + + spktlen = ngtcp2_conn_write_connection_close(conn, NULL, NULL, buf, + sizeof(buf), &ccerr, 0); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(spktlen < NGTCP2_MAX_UDP_PAYLOAD_SIZE); + + p = buf; + + shdlen = ngtcp2_pkt_decode_hd_long(&hd, p, (size_t)spktlen); + + CU_ASSERT(shdlen > 0); + CU_ASSERT(NGTCP2_PKT_HANDSHAKE == hd.type); + + p += shdlen + (ngtcp2_ssize)hd.len; + spktlen -= shdlen + (ngtcp2_ssize)hd.len; + + shdlen = ngtcp2_pkt_decode_hd_short(&hd, p, (size_t)spktlen, + conn->dcid.current.cid.datalen); + CU_ASSERT(shdlen > 0); + CU_ASSERT(NGTCP2_PKT_1RTT == hd.type); + + ngtcp2_conn_del(conn); + + /* Client has confirmed handshake */ + setup_default_client(&conn); + + ngtcp2_connection_close_error_set_transport_error(&ccerr, NGTCP2_NO_ERROR, + NULL, 0); + + spktlen = ngtcp2_conn_write_connection_close(conn, NULL, NULL, buf, + sizeof(buf), &ccerr, 0); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(spktlen < NGTCP2_MAX_UDP_PAYLOAD_SIZE); + + shdlen = ngtcp2_pkt_decode_hd_short(&hd, buf, (size_t)spktlen, + conn->dcid.current.cid.datalen); + + CU_ASSERT(shdlen > 0); + CU_ASSERT(NGTCP2_PKT_1RTT == hd.type); + + ngtcp2_conn_del(conn); + + /* Server has Initial and Handshake key */ + setup_handshake_server(&conn); + + conn->dcid.current.bytes_recv = NGTCP2_MAX_UDP_PAYLOAD_SIZE; + + init_initial_crypto_ctx(&crypto_ctx); + + ngtcp2_conn_set_initial_crypto_ctx(conn, &crypto_ctx); + ngtcp2_conn_install_initial_key(conn, &aead_ctx, null_iv, &hp_ctx, &aead_ctx, + null_iv, &hp_ctx, sizeof(null_iv)); + + init_crypto_ctx(&crypto_ctx); + + ngtcp2_conn_set_crypto_ctx(conn, &crypto_ctx); + conn->negotiated_version = conn->client_chosen_version; + ngtcp2_conn_install_tx_handshake_key(conn, &aead_ctx, null_iv, + sizeof(null_iv), &hp_ctx); + + ngtcp2_connection_close_error_set_transport_error(&ccerr, NGTCP2_NO_ERROR, + NULL, 0); + + spktlen = ngtcp2_conn_write_connection_close(conn, NULL, NULL, buf, + sizeof(buf), &ccerr, 0); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(spktlen < NGTCP2_MAX_UDP_PAYLOAD_SIZE); + + p = buf; + + shdlen = ngtcp2_pkt_decode_hd_long(&hd, p, (size_t)spktlen); + + CU_ASSERT(shdlen > 0); + CU_ASSERT(NGTCP2_PKT_INITIAL == hd.type); + + p += shdlen + (ngtcp2_ssize)hd.len; + spktlen -= shdlen + (ngtcp2_ssize)hd.len; + + shdlen = ngtcp2_pkt_decode_hd_long(&hd, p, (size_t)spktlen); + + CU_ASSERT(shdlen > 0); + CU_ASSERT(NGTCP2_PKT_HANDSHAKE == hd.type); + CU_ASSERT(shdlen + (ngtcp2_ssize)hd.len == spktlen); + + ngtcp2_conn_del(conn); + + /* Server has all keys and has not confirmed handshake */ + setup_handshake_server(&conn); + + conn->dcid.current.bytes_recv = NGTCP2_MAX_UDP_PAYLOAD_SIZE; + + init_initial_crypto_ctx(&crypto_ctx); + + ngtcp2_conn_set_initial_crypto_ctx(conn, &crypto_ctx); + ngtcp2_conn_install_initial_key(conn, &aead_ctx, null_iv, &hp_ctx, &aead_ctx, + null_iv, &hp_ctx, sizeof(null_iv)); + + init_crypto_ctx(&crypto_ctx); + + ngtcp2_conn_set_crypto_ctx(conn, &crypto_ctx); + conn->negotiated_version = conn->client_chosen_version; + ngtcp2_conn_install_tx_handshake_key(conn, &aead_ctx, null_iv, + sizeof(null_iv), &hp_ctx); + ngtcp2_conn_install_tx_key(conn, null_secret, sizeof(null_secret), &aead_ctx, + null_iv, sizeof(null_iv), &hp_ctx); + + conn->state = NGTCP2_CS_POST_HANDSHAKE; + + ngtcp2_connection_close_error_set_transport_error(&ccerr, NGTCP2_NO_ERROR, + NULL, 0); + + spktlen = ngtcp2_conn_write_connection_close(conn, NULL, NULL, buf, + sizeof(buf), &ccerr, 0); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(spktlen < NGTCP2_MAX_UDP_PAYLOAD_SIZE); + + p = buf; + + shdlen = ngtcp2_pkt_decode_hd_long(&hd, p, (size_t)spktlen); + + CU_ASSERT(shdlen > 0); + CU_ASSERT(NGTCP2_PKT_INITIAL == hd.type); + + p += shdlen + (ngtcp2_ssize)hd.len; + spktlen -= shdlen + (ngtcp2_ssize)hd.len; + + shdlen = ngtcp2_pkt_decode_hd_long(&hd, p, (size_t)spktlen); + + CU_ASSERT(shdlen > 0); + CU_ASSERT(NGTCP2_PKT_HANDSHAKE == hd.type); + + p += shdlen + (ngtcp2_ssize)hd.len; + spktlen -= shdlen + (ngtcp2_ssize)hd.len; + + shdlen = ngtcp2_pkt_decode_hd_short(&hd, p, (size_t)spktlen, + conn->dcid.current.cid.datalen); + + CU_ASSERT(shdlen > 0); + CU_ASSERT(NGTCP2_PKT_1RTT == hd.type); + + ngtcp2_conn_del(conn); + + /* Server has confirmed handshake */ + setup_default_server(&conn); + + ngtcp2_connection_close_error_set_transport_error(&ccerr, NGTCP2_NO_ERROR, + NULL, 0); + + spktlen = ngtcp2_conn_write_connection_close(conn, NULL, NULL, buf, + sizeof(buf), &ccerr, 0); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(spktlen < NGTCP2_MAX_UDP_PAYLOAD_SIZE); + + shdlen = ngtcp2_pkt_decode_hd_short(&hd, buf, (size_t)spktlen, + conn->dcid.current.cid.datalen); + + CU_ASSERT(shdlen > 0); + CU_ASSERT(NGTCP2_PKT_1RTT == hd.type); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_write_application_close(void) { + ngtcp2_conn *conn; + uint8_t buf[1200]; + ngtcp2_ssize spktlen, shdlen; + ngtcp2_pkt_hd hd; + const uint8_t *p; + ngtcp2_crypto_aead_ctx aead_ctx = {0}; + ngtcp2_crypto_cipher_ctx hp_ctx = {0}; + uint64_t app_err_code = 0; + ngtcp2_crypto_ctx crypto_ctx; + ngtcp2_connection_close_error ccerr; + + /* Client only Initial key */ + setup_handshake_client(&conn); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), 0); + + CU_ASSERT(spktlen > 0); + + ngtcp2_connection_close_error_set_application_error( + &ccerr, app_err_code, (const uint8_t *)"foo", 3); + + spktlen = ngtcp2_conn_write_connection_close(conn, NULL, NULL, buf, + sizeof(buf), &ccerr, 0); + + CU_ASSERT(spktlen >= NGTCP2_MAX_UDP_PAYLOAD_SIZE); + + shdlen = ngtcp2_pkt_decode_hd_long(&hd, buf, (size_t)spktlen); + + CU_ASSERT(shdlen > 0); + CU_ASSERT(NGTCP2_PKT_INITIAL == hd.type); + CU_ASSERT(shdlen + (ngtcp2_ssize)hd.len == spktlen); + + ngtcp2_conn_del(conn); + + /* Client has Initial and Handshake keys */ + setup_handshake_client(&conn); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), 0); + + CU_ASSERT(spktlen > 0); + + init_crypto_ctx(&crypto_ctx); + + ngtcp2_conn_set_crypto_ctx(conn, &crypto_ctx); + conn->negotiated_version = conn->client_chosen_version; + ngtcp2_conn_install_tx_handshake_key(conn, &aead_ctx, null_iv, + sizeof(null_iv), &hp_ctx); + + ngtcp2_connection_close_error_set_application_error(&ccerr, app_err_code, + NULL, 0); + + spktlen = ngtcp2_conn_write_connection_close(conn, NULL, NULL, buf, + sizeof(buf), &ccerr, 0); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(spktlen < NGTCP2_MAX_UDP_PAYLOAD_SIZE); + + shdlen = ngtcp2_pkt_decode_hd_long(&hd, buf, (size_t)spktlen); + + CU_ASSERT(shdlen > 0); + CU_ASSERT(NGTCP2_PKT_HANDSHAKE == hd.type); + CU_ASSERT(shdlen + (ngtcp2_ssize)hd.len == spktlen); + + ngtcp2_conn_del(conn); + + /* Client has all keys and has not confirmed handshake */ + setup_handshake_client(&conn); + + init_crypto_ctx(&crypto_ctx); + + ngtcp2_conn_set_crypto_ctx(conn, &crypto_ctx); + conn->negotiated_version = conn->client_chosen_version; + ngtcp2_conn_install_tx_handshake_key(conn, &aead_ctx, null_iv, + sizeof(null_iv), &hp_ctx); + ngtcp2_conn_install_tx_key(conn, null_secret, sizeof(null_secret), &aead_ctx, + null_iv, sizeof(null_iv), &hp_ctx); + + conn->state = NGTCP2_CS_POST_HANDSHAKE; + + ngtcp2_connection_close_error_set_application_error(&ccerr, app_err_code, + NULL, 0); + + spktlen = ngtcp2_conn_write_connection_close(conn, NULL, NULL, buf, + sizeof(buf), &ccerr, 0); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(spktlen < NGTCP2_MAX_UDP_PAYLOAD_SIZE); + + p = buf; + + shdlen = ngtcp2_pkt_decode_hd_long(&hd, p, (size_t)spktlen); + + CU_ASSERT(shdlen > 0); + CU_ASSERT(NGTCP2_PKT_HANDSHAKE == hd.type); + + p += shdlen + (ngtcp2_ssize)hd.len; + spktlen -= shdlen + (ngtcp2_ssize)hd.len; + + shdlen = ngtcp2_pkt_decode_hd_short(&hd, p, (size_t)spktlen, + conn->dcid.current.cid.datalen); + CU_ASSERT(shdlen > 0); + CU_ASSERT(NGTCP2_PKT_1RTT == hd.type); + + ngtcp2_conn_del(conn); + + /* Client has confirmed handshake */ + setup_default_client(&conn); + + ngtcp2_connection_close_error_set_application_error(&ccerr, app_err_code, + NULL, 0); + + spktlen = ngtcp2_conn_write_connection_close(conn, NULL, NULL, buf, + sizeof(buf), &ccerr, 0); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(spktlen < NGTCP2_MAX_UDP_PAYLOAD_SIZE); + + shdlen = ngtcp2_pkt_decode_hd_short(&hd, buf, (size_t)spktlen, + conn->dcid.current.cid.datalen); + + CU_ASSERT(shdlen > 0); + CU_ASSERT(NGTCP2_PKT_1RTT == hd.type); + + ngtcp2_conn_del(conn); + + /* Server has Initial and Handshake key */ + setup_handshake_server(&conn); + + conn->dcid.current.bytes_recv = NGTCP2_MAX_UDP_PAYLOAD_SIZE; + + init_initial_crypto_ctx(&crypto_ctx); + + ngtcp2_conn_set_initial_crypto_ctx(conn, &crypto_ctx); + ngtcp2_conn_install_initial_key(conn, &aead_ctx, null_iv, &hp_ctx, &aead_ctx, + null_iv, &hp_ctx, sizeof(null_iv)); + + init_crypto_ctx(&crypto_ctx); + + ngtcp2_conn_set_crypto_ctx(conn, &crypto_ctx); + conn->negotiated_version = conn->client_chosen_version; + ngtcp2_conn_install_tx_handshake_key(conn, &aead_ctx, null_iv, + sizeof(null_iv), &hp_ctx); + + ngtcp2_connection_close_error_set_application_error(&ccerr, app_err_code, + NULL, 0); + + spktlen = ngtcp2_conn_write_connection_close(conn, NULL, NULL, buf, + sizeof(buf), &ccerr, 0); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(spktlen < NGTCP2_MAX_UDP_PAYLOAD_SIZE); + + p = buf; + + shdlen = ngtcp2_pkt_decode_hd_long(&hd, p, (size_t)spktlen); + + CU_ASSERT(shdlen > 0); + CU_ASSERT(NGTCP2_PKT_INITIAL == hd.type); + + p += shdlen + (ngtcp2_ssize)hd.len; + spktlen -= shdlen + (ngtcp2_ssize)hd.len; + + shdlen = ngtcp2_pkt_decode_hd_long(&hd, p, (size_t)spktlen); + + CU_ASSERT(shdlen > 0); + CU_ASSERT(NGTCP2_PKT_HANDSHAKE == hd.type); + CU_ASSERT(shdlen + (ngtcp2_ssize)hd.len == spktlen); + + ngtcp2_conn_del(conn); + + /* Server has all keys and has not confirmed handshake */ + setup_handshake_server(&conn); + + conn->dcid.current.bytes_recv = NGTCP2_MAX_UDP_PAYLOAD_SIZE; + + init_initial_crypto_ctx(&crypto_ctx); + + ngtcp2_conn_set_initial_crypto_ctx(conn, &crypto_ctx); + ngtcp2_conn_install_initial_key(conn, &aead_ctx, null_iv, &hp_ctx, &aead_ctx, + null_iv, &hp_ctx, sizeof(null_iv)); + + init_crypto_ctx(&crypto_ctx); + + ngtcp2_conn_set_crypto_ctx(conn, &crypto_ctx); + conn->negotiated_version = conn->client_chosen_version; + ngtcp2_conn_install_tx_handshake_key(conn, &aead_ctx, null_iv, + sizeof(null_iv), &hp_ctx); + ngtcp2_conn_install_tx_key(conn, null_secret, sizeof(null_secret), &aead_ctx, + null_iv, sizeof(null_iv), &hp_ctx); + + ngtcp2_connection_close_error_set_application_error(&ccerr, app_err_code, + NULL, 0); + + spktlen = ngtcp2_conn_write_connection_close(conn, NULL, NULL, buf, + sizeof(buf), &ccerr, 0); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(spktlen < NGTCP2_MAX_UDP_PAYLOAD_SIZE); + + p = buf; + + shdlen = ngtcp2_pkt_decode_hd_long(&hd, p, (size_t)spktlen); + + CU_ASSERT(shdlen > 0); + CU_ASSERT(NGTCP2_PKT_INITIAL == hd.type); + + p += shdlen + (ngtcp2_ssize)hd.len; + spktlen -= shdlen + (ngtcp2_ssize)hd.len; + + shdlen = ngtcp2_pkt_decode_hd_long(&hd, p, (size_t)spktlen); + + CU_ASSERT(shdlen > 0); + CU_ASSERT(NGTCP2_PKT_HANDSHAKE == hd.type); + + p += shdlen + (ngtcp2_ssize)hd.len; + spktlen -= shdlen + (ngtcp2_ssize)hd.len; + + shdlen = ngtcp2_pkt_decode_hd_short(&hd, p, (size_t)spktlen, + conn->dcid.current.cid.datalen); + + CU_ASSERT(shdlen > 0); + CU_ASSERT(NGTCP2_PKT_1RTT == hd.type); + + ngtcp2_conn_del(conn); + + /* Server has confirmed handshake */ + setup_default_server(&conn); + + ngtcp2_connection_close_error_set_application_error(&ccerr, app_err_code, + NULL, 0); + + spktlen = ngtcp2_conn_write_connection_close(conn, NULL, NULL, buf, + sizeof(buf), &ccerr, 0); + + CU_ASSERT(spktlen > 0); + CU_ASSERT(spktlen < NGTCP2_MAX_UDP_PAYLOAD_SIZE); + + shdlen = ngtcp2_pkt_decode_hd_short(&hd, buf, (size_t)spktlen, + conn->dcid.current.cid.datalen); + + CU_ASSERT(shdlen > 0); + CU_ASSERT(NGTCP2_PKT_1RTT == hd.type); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_rtb_reclaim_on_pto(void) { + ngtcp2_conn *conn; + int rv; + int64_t stream_id; + uint8_t buf[2048]; + ngtcp2_ssize nwrite; + ngtcp2_ssize spktlen; + size_t i; + size_t num_reclaim_pkt; + ngtcp2_rtb_entry *ent; + ngtcp2_ksl_it it; + + setup_default_client(&conn); + + rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + + CU_ASSERT(0 == rv); + + for (i = 0; i < 5; ++i) { + spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), + &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE, + stream_id, null_data, 1024, 1); + + CU_ASSERT(0 < spktlen); + } + + CU_ASSERT(5 == ngtcp2_ksl_len(&conn->pktns.rtb.ents)); + + rv = ngtcp2_conn_on_loss_detection_timer(conn, 3 * NGTCP2_SECONDS); + + CU_ASSERT(0 == rv); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), + 3 * NGTCP2_SECONDS); + + CU_ASSERT(spktlen > 0); + + it = ngtcp2_ksl_begin(&conn->pktns.rtb.ents); + num_reclaim_pkt = 0; + for (; !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) { + ent = ngtcp2_ksl_it_get(&it); + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED) { + ++num_reclaim_pkt; + } + } + + CU_ASSERT(1 == num_reclaim_pkt); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_rtb_reclaim_on_pto_datagram(void) { + ngtcp2_conn *conn; + int rv; + int64_t stream_id; + uint8_t buf[2048]; + ngtcp2_ssize nwrite; + ngtcp2_ssize spktlen; + size_t num_reclaim_pkt; + ngtcp2_rtb_entry *ent; + ngtcp2_ksl_it it; + ngtcp2_vec datav; + int accepted; + ngtcp2_frame_chain *frc; + + /* DATAGRAM frame must not be reclaimed on PTO */ + setup_default_client(&conn); + + conn->callbacks.ack_datagram = ack_datagram; + conn->remote.transport_params->max_datagram_frame_size = 65535; + + rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + + CU_ASSERT(0 == rv); + + spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), + &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE, + stream_id, null_data, 1024, 1); + + CU_ASSERT(0 < spktlen); + + datav.base = null_data; + datav.len = 10; + + spktlen = ngtcp2_conn_writev_datagram( + conn, NULL, NULL, buf, sizeof(buf), &accepted, + NGTCP2_WRITE_DATAGRAM_FLAG_NONE, 1000000007, &datav, 1, 1); + + CU_ASSERT(accepted); + CU_ASSERT(0 < spktlen); + CU_ASSERT(2 == ngtcp2_ksl_len(&conn->pktns.rtb.ents)); + + rv = ngtcp2_conn_on_loss_detection_timer(conn, 3 * NGTCP2_SECONDS); + + CU_ASSERT(0 == rv); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), + 3 * NGTCP2_SECONDS); + + CU_ASSERT(spktlen > 0); + + it = ngtcp2_ksl_begin(&conn->pktns.rtb.ents); + num_reclaim_pkt = 0; + for (; !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) { + ent = ngtcp2_ksl_it_get(&it); + if (ent->flags & NGTCP2_RTB_ENTRY_FLAG_PTO_RECLAIMED) { + ++num_reclaim_pkt; + for (frc = ent->frc; frc; frc = frc->next) { + CU_ASSERT(NGTCP2_FRAME_DATAGRAM != frc->fr.type); + } + } + } + + CU_ASSERT(1 == num_reclaim_pkt); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_validate_ecn(void) { + ngtcp2_conn *conn; + uint8_t buf[2048]; + ngtcp2_ssize spktlen; + ngtcp2_pkt_info pi; + size_t pktlen; + int rv; + ngtcp2_frame fr; + int64_t stream_id; + ngtcp2_ssize nwrite; + size_t i; + ngtcp2_tstamp t = 0; + + setup_default_client(&conn); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, &pi, buf, sizeof(buf), 1); + + CU_ASSERT(0 < spktlen); + CU_ASSERT(NGTCP2_ECN_ECT_0 == pi.ecn); + CU_ASSERT(NGTCP2_ECN_STATE_TESTING == conn->tx.ecn.state); + CU_ASSERT(1 == conn->tx.ecn.validation_start_ts); + CU_ASSERT(0 == conn->pktns.tx.ecn.start_pkt_num); + + fr.type = NGTCP2_FRAME_ACK_ECN; + fr.ack.largest_ack = 0; + fr.ack.ack_delay = 0; + fr.ack.first_ack_range = 0; + fr.ack.rangecnt = 0; + fr.ack.ecn.ect0 = 1; + fr.ack.ecn.ect1 = 0; + fr.ack.ecn.ce = 0; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 0, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2); + + CU_ASSERT(0 == rv); + CU_ASSERT(NGTCP2_ECN_STATE_CAPABLE == conn->tx.ecn.state); + + rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + + CU_ASSERT(0 == rv); + + spktlen = ngtcp2_conn_write_stream(conn, NULL, &pi, buf, sizeof(buf), &nwrite, + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + null_data, 1024, 2); + + CU_ASSERT(0 < spktlen); + + /* Receiving ACK frame containing less ECN counts fails + validation */ + fr.type = NGTCP2_FRAME_ACK_ECN; + fr.ack.largest_ack = 1; + fr.ack.ack_delay = 0; + fr.ack.first_ack_range = 0; + fr.ack.rangecnt = 0; + fr.ack.ecn.ect0 = 0; + fr.ack.ecn.ect1 = 0; + fr.ack.ecn.ce = 0; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 1, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 3); + + CU_ASSERT(0 == rv); + CU_ASSERT(NGTCP2_ECN_STATE_FAILED == conn->tx.ecn.state); + + spktlen = ngtcp2_conn_write_stream(conn, NULL, &pi, buf, sizeof(buf), &nwrite, + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + null_data, 1024, 2); + + CU_ASSERT(0 < spktlen); + CU_ASSERT(NGTCP2_ECN_NOT_ECT == pi.ecn); + + ngtcp2_conn_del(conn); + + /* Receiving ACK frame without ECN counts invalidates ECN + capability */ + setup_default_server(&conn); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, &pi, buf, sizeof(buf), 1); + + CU_ASSERT(0 < spktlen); + CU_ASSERT(NGTCP2_ECN_ECT_0 == pi.ecn); + CU_ASSERT(NGTCP2_ECN_STATE_TESTING == conn->tx.ecn.state); + CU_ASSERT(1 == conn->tx.ecn.validation_start_ts); + CU_ASSERT(0 == conn->pktns.tx.ecn.start_pkt_num); + + fr.type = NGTCP2_FRAME_ACK; + fr.ack.largest_ack = 0; + fr.ack.ack_delay = 0; + fr.ack.first_ack_range = 0; + fr.ack.rangecnt = 0; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 0, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2); + + CU_ASSERT(0 == rv); + CU_ASSERT(NGTCP2_ECN_STATE_FAILED == conn->tx.ecn.state); + + ngtcp2_conn_del(conn); + + /* CE counts must be considered */ + setup_default_client(&conn); + + rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + + CU_ASSERT(0 == rv); + + for (i = 0; i < 2; ++i) { + spktlen = ngtcp2_conn_write_stream(conn, NULL, &pi, buf, sizeof(buf), + &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE, + stream_id, null_data, 1024, 2); + + CU_ASSERT(0 < spktlen); + CU_ASSERT(NGTCP2_ECN_ECT_0 == pi.ecn); + } + + CU_ASSERT(NGTCP2_ECN_STATE_TESTING == conn->tx.ecn.state); + CU_ASSERT(2 == conn->tx.ecn.validation_start_ts); + CU_ASSERT(0 == conn->pktns.tx.ecn.start_pkt_num); + + fr.type = NGTCP2_FRAME_ACK_ECN; + fr.ack.largest_ack = 1; + fr.ack.ack_delay = 0; + fr.ack.first_ack_range = 1; + fr.ack.rangecnt = 0; + fr.ack.ecn.ect0 = 1; + fr.ack.ecn.ect1 = 0; + fr.ack.ecn.ce = 1; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 0, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2); + + CU_ASSERT(0 == rv); + CU_ASSERT(NGTCP2_ECN_STATE_CAPABLE == conn->tx.ecn.state); + CU_ASSERT(0 == ngtcp2_ksl_len(&conn->pktns.rtb.ents)); + + ngtcp2_conn_del(conn); + + /* If increments of ECN counts is less than the number of + acknowledged ECN entries, ECN validation fails. */ + setup_default_client(&conn); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, &pi, buf, sizeof(buf), 1); + + CU_ASSERT(0 < spktlen); + CU_ASSERT(NGTCP2_ECN_ECT_0 == pi.ecn); + CU_ASSERT(NGTCP2_ECN_STATE_TESTING == conn->tx.ecn.state); + CU_ASSERT(1 == conn->tx.ecn.validation_start_ts); + CU_ASSERT(0 == conn->pktns.tx.ecn.start_pkt_num); + + fr.type = NGTCP2_FRAME_ACK_ECN; + fr.ack.largest_ack = 0; + fr.ack.ack_delay = 0; + fr.ack.first_ack_range = 0; + fr.ack.rangecnt = 0; + fr.ack.ecn.ect0 = 0; + fr.ack.ecn.ect1 = 1; + fr.ack.ecn.ce = 0; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 0, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2); + + CU_ASSERT(0 == rv); + CU_ASSERT(NGTCP2_ECN_STATE_FAILED == conn->tx.ecn.state); + + ngtcp2_conn_del(conn); + + /* If ECT count is larger than the number of ECT marked packet, ECN + validation fails. */ + setup_default_client(&conn); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, &pi, buf, sizeof(buf), 1); + + CU_ASSERT(0 < spktlen); + CU_ASSERT(NGTCP2_ECN_ECT_0 == pi.ecn); + CU_ASSERT(NGTCP2_ECN_STATE_TESTING == conn->tx.ecn.state); + CU_ASSERT(1 == conn->tx.ecn.validation_start_ts); + CU_ASSERT(0 == conn->pktns.tx.ecn.start_pkt_num); + + fr.type = NGTCP2_FRAME_ACK_ECN; + fr.ack.largest_ack = 0; + fr.ack.ack_delay = 0; + fr.ack.first_ack_range = 0; + fr.ack.rangecnt = 0; + fr.ack.ecn.ect0 = 2; + fr.ack.ecn.ect1 = 0; + fr.ack.ecn.ce = 0; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 0, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, 2); + + CU_ASSERT(0 == rv); + CU_ASSERT(NGTCP2_ECN_STATE_FAILED == conn->tx.ecn.state); + + ngtcp2_conn_del(conn); + + /* ECN validation fails if all ECN marked packets are lost */ + setup_default_client(&conn); + + t = 0; + + rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + + CU_ASSERT(0 == rv); + + for (i = 0; i < NGTCP2_ECN_MAX_NUM_VALIDATION_PKTS; ++i) { + spktlen = ngtcp2_conn_write_stream(conn, NULL, &pi, buf, sizeof(buf), + &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE, + stream_id, null_data, 25, t); + + CU_ASSERT(0 < spktlen); + CU_ASSERT(NGTCP2_ECN_ECT_0 == pi.ecn); + } + + CU_ASSERT(NGTCP2_ECN_STATE_UNKNOWN == conn->tx.ecn.state); + CU_ASSERT(NGTCP2_ECN_MAX_NUM_VALIDATION_PKTS == conn->tx.ecn.dgram_sent); + + t += NGTCP2_MILLISECONDS; + + spktlen = ngtcp2_conn_write_stream(conn, NULL, &pi, buf, sizeof(buf), &nwrite, + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + null_data, 25, t); + + CU_ASSERT(0 < spktlen); + CU_ASSERT(NGTCP2_ECN_NOT_ECT == pi.ecn); + CU_ASSERT(NGTCP2_ECN_STATE_UNKNOWN == conn->tx.ecn.state); + CU_ASSERT(0 == conn->tx.ecn.validation_start_ts); + CU_ASSERT(0 == conn->pktns.tx.ecn.start_pkt_num); + CU_ASSERT(NGTCP2_ECN_MAX_NUM_VALIDATION_PKTS == conn->tx.ecn.dgram_sent); + CU_ASSERT(0 == conn->pktns.tx.ecn.validation_pkt_lost); + + fr.type = NGTCP2_FRAME_ACK; + fr.ack.largest_ack = NGTCP2_ECN_MAX_NUM_VALIDATION_PKTS; + fr.ack.ack_delay = 0; + fr.ack.first_ack_range = 0; + fr.ack.rangecnt = 0; + + t += NGTCP2_MILLISECONDS; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 0, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, t); + + CU_ASSERT(0 == rv); + + CU_ASSERT(NGTCP2_ECN_STATE_FAILED == conn->tx.ecn.state); + CU_ASSERT(NGTCP2_ECN_MAX_NUM_VALIDATION_PKTS == + conn->pktns.tx.ecn.validation_pkt_lost); + + ngtcp2_conn_del(conn); + + /* ECN validation fails if all ECN marked packets sent in last 3 * + RTT are lost */ + setup_default_client(&conn); + + rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + + CU_ASSERT(0 == rv); + + for (i = 0; i < 2; ++i) { + spktlen = ngtcp2_conn_write_stream(conn, NULL, &pi, buf, sizeof(buf), + &nwrite, NGTCP2_WRITE_STREAM_FLAG_NONE, + stream_id, null_data, 25, 0); + + CU_ASSERT(0 < spktlen); + CU_ASSERT(NGTCP2_ECN_ECT_0 == pi.ecn); + } + + CU_ASSERT(NGTCP2_ECN_STATE_TESTING == conn->tx.ecn.state); + CU_ASSERT(2 == conn->tx.ecn.dgram_sent); + + spktlen = ngtcp2_conn_write_stream(conn, NULL, &pi, buf, sizeof(buf), &nwrite, + NGTCP2_WRITE_STREAM_FLAG_NONE, stream_id, + null_data, 25, 3 * NGTCP2_SECONDS); + + CU_ASSERT(0 < spktlen); + CU_ASSERT(NGTCP2_ECN_NOT_ECT == pi.ecn); + CU_ASSERT(NGTCP2_ECN_STATE_UNKNOWN == conn->tx.ecn.state); + CU_ASSERT(0 == conn->tx.ecn.validation_start_ts); + CU_ASSERT(0 == conn->pktns.tx.ecn.start_pkt_num); + CU_ASSERT(2 == conn->pktns.tx.ecn.validation_pkt_sent); + CU_ASSERT(0 == conn->pktns.tx.ecn.validation_pkt_lost); + + fr.type = NGTCP2_FRAME_ACK; + fr.ack.largest_ack = 2; + fr.ack.ack_delay = 0; + fr.ack.first_ack_range = 0; + fr.ack.rangecnt = 0; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, 0, &fr, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, + 4 * NGTCP2_SECONDS); + + CU_ASSERT(0 == rv); + CU_ASSERT(NGTCP2_ECN_STATE_FAILED == conn->tx.ecn.state); + CU_ASSERT(2 == conn->pktns.tx.ecn.validation_pkt_lost); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_path_validation(void) { + ngtcp2_conn *conn; + uint8_t buf[2048]; + size_t pktlen; + ngtcp2_ssize spktlen; + ngtcp2_tstamp t = 0; + int64_t pkt_num = 0; + ngtcp2_frame frs[4]; + int rv; + ngtcp2_path_storage rpath, wpath; + ngtcp2_pv_entry *ent; + + /* server starts path validation in NAT rebinding scenario. */ + setup_default_server(&conn); + + /* This will send NEW_CONNECTION_ID frames */ + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + + frs[0].type = NGTCP2_FRAME_PING; + + /* Just change remote port */ + path_init(&rpath, 0, 0, 0, 1); + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &rpath.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(NULL != conn->pv); + CU_ASSERT(0 == conn->pv->dcid.seq); + CU_ASSERT(ngtcp2_path_eq(&conn->pv->dcid.ps.path, &rpath.path)); + + ngtcp2_path_storage_zero(&wpath); + spktlen = + ngtcp2_conn_write_pkt(conn, &wpath.path, NULL, buf, sizeof(buf), ++t); + + /* Server has not received enough bytes to pad probing packet. */ + CU_ASSERT(1200 > spktlen); + CU_ASSERT(ngtcp2_path_eq(&rpath.path, &wpath.path)); + CU_ASSERT(1 == ngtcp2_ringbuf_len(&conn->pv->ents.rb)); + + ent = ngtcp2_ringbuf_get(&conn->pv->ents.rb, 0); + + CU_ASSERT(ent->flags & NGTCP2_PV_ENTRY_FLAG_UNDERSIZED); + CU_ASSERT(conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE); + CU_ASSERT(!(conn->pv->flags & NGTCP2_PV_FLAG_MTU_PROBE)); + + frs[0].type = NGTCP2_FRAME_PATH_RESPONSE; + memcpy(frs[0].path_response.data, ent->data, sizeof(ent->data)); + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &rpath.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + /* Start another path validation to probe least MTU */ + CU_ASSERT(NULL != conn->pv); + CU_ASSERT(conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE); + CU_ASSERT(conn->pv->flags & NGTCP2_PV_FLAG_MTU_PROBE); + + ngtcp2_path_storage_zero(&wpath); + spktlen = + ngtcp2_conn_write_pkt(conn, &wpath.path, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(1200 <= spktlen); + CU_ASSERT(ngtcp2_path_eq(&rpath.path, &wpath.path)); + CU_ASSERT(1 == ngtcp2_ringbuf_len(&conn->pv->ents.rb)); + + ent = ngtcp2_ringbuf_get(&conn->pv->ents.rb, 0); + frs[0].type = NGTCP2_FRAME_PATH_RESPONSE; + memcpy(frs[0].path_response.data, ent->data, sizeof(ent->data)); + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 1, + conn->pktns.crypto.rx.ckm); + rv = ngtcp2_conn_read_pkt(conn, &rpath.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + /* Now perform another validation to old path */ + CU_ASSERT(NULL != conn->pv); + CU_ASSERT(!(conn->pv->flags & NGTCP2_PV_FLAG_FALLBACK_ON_FAILURE)); + CU_ASSERT(!(conn->pv->flags & NGTCP2_PV_FLAG_MTU_PROBE)); + CU_ASSERT(conn->pv->flags & NGTCP2_PV_FLAG_DONT_CARE); + + ngtcp2_path_storage_zero(&wpath); + spktlen = + ngtcp2_conn_write_pkt(conn, &wpath.path, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(1200 <= spktlen); + CU_ASSERT(ngtcp2_path_eq(&null_path.path, &wpath.path)); + CU_ASSERT(1 == ngtcp2_ringbuf_len(&conn->pv->ents.rb)); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_early_data_sync_stream_data_limit(void) { + ngtcp2_conn *conn; + uint8_t buf[1024]; + ngtcp2_ssize spktlen; + ngtcp2_ssize datalen; + int64_t bidi_stream_id, uni_stream_id; + int rv; + ngtcp2_frame fr; + size_t pktlen; + ngtcp2_crypto_aead_ctx aead_ctx = {0}; + ngtcp2_crypto_cipher_ctx hp_ctx = {0}; + ngtcp2_transport_params params; + ngtcp2_strm *strm; + ngtcp2_tstamp t = 0; + + setup_early_client(&conn); + + rv = ngtcp2_conn_open_bidi_stream(conn, &bidi_stream_id, NULL); + + CU_ASSERT(0 == rv); + + spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), + &datalen, NGTCP2_WRITE_STREAM_FLAG_FIN, + bidi_stream_id, null_data, 1024, ++t); + + CU_ASSERT((ngtcp2_ssize)sizeof(buf) == spktlen); + CU_ASSERT(670 == datalen); + + rv = ngtcp2_conn_open_uni_stream(conn, &uni_stream_id, NULL); + + CU_ASSERT(0 == rv); + + spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), + &datalen, NGTCP2_WRITE_STREAM_FLAG_FIN, + uni_stream_id, null_data, 1024, ++t); + + CU_ASSERT((ngtcp2_ssize)sizeof(buf) == spktlen); + CU_ASSERT(958); + + fr.type = NGTCP2_FRAME_CRYPTO; + fr.crypto.offset = 0; + fr.crypto.datacnt = 1; + fr.crypto.data[0].len = 198; + fr.crypto.data[0].base = null_data; + + pktlen = write_initial_pkt(buf, sizeof(buf), &conn->oscid, + ngtcp2_conn_get_dcid(conn), 0, NGTCP2_PROTO_VER_V1, + NULL, 0, &fr, 1, &null_ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + rv = ngtcp2_conn_install_rx_handshake_key(conn, &aead_ctx, null_iv, + sizeof(null_iv), &hp_ctx); + + CU_ASSERT(0 == rv); + + rv = ngtcp2_conn_install_tx_handshake_key(conn, &aead_ctx, null_iv, + sizeof(null_iv), &hp_ctx); + + CU_ASSERT(0 == rv); + + rv = ngtcp2_conn_install_rx_key(conn, null_secret, sizeof(null_secret), + &aead_ctx, null_iv, sizeof(null_iv), &hp_ctx); + + CU_ASSERT(0 == rv); + + memset(¶ms, 0, sizeof(params)); + ngtcp2_cid_init(¶ms.initial_scid, conn->dcid.current.cid.data, + conn->dcid.current.cid.datalen); + ngtcp2_cid_init(¶ms.original_dcid, conn->rcid.data, conn->rcid.datalen); + params.max_udp_payload_size = 1200; + params.initial_max_stream_data_bidi_local = + conn->early.transport_params.initial_max_stream_data_bidi_local; + params.initial_max_stream_data_bidi_remote = 640 * 1024; + params.initial_max_stream_data_uni = 320 * 1024; + params.initial_max_data = conn->early.transport_params.initial_max_data; + params.initial_max_streams_bidi = + conn->early.transport_params.initial_max_streams_bidi; + params.initial_max_streams_uni = + conn->early.transport_params.initial_max_streams_uni; + params.active_connection_id_limit = + conn->early.transport_params.active_connection_id_limit; + + rv = ngtcp2_conn_set_remote_transport_params(conn, ¶ms); + + CU_ASSERT(0 == rv); + + rv = ngtcp2_conn_install_tx_key(conn, null_secret, sizeof(null_secret), + &aead_ctx, null_iv, sizeof(null_iv), &hp_ctx); + + CU_ASSERT(0 == rv); + + ngtcp2_conn_handshake_completed(conn); + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(0 < spktlen); + + strm = ngtcp2_conn_find_stream(conn, bidi_stream_id); + + CU_ASSERT(params.initial_max_stream_data_bidi_remote == strm->tx.max_offset); + + strm = ngtcp2_conn_find_stream(conn, uni_stream_id); + + CU_ASSERT(params.initial_max_stream_data_uni == strm->tx.max_offset); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_early_data_rejected(void) { + ngtcp2_conn *conn; + uint8_t buf[1024]; + ngtcp2_ssize spktlen; + ngtcp2_ssize datalen; + int64_t bidi_stream_id, uni_stream_id; + int rv; + ngtcp2_frame fr; + size_t pktlen; + ngtcp2_crypto_aead_ctx aead_ctx = {0}; + ngtcp2_crypto_cipher_ctx hp_ctx = {0}; + ngtcp2_transport_params params; + ngtcp2_tstamp t = 0; + + setup_early_client(&conn); + + rv = ngtcp2_conn_open_bidi_stream(conn, &bidi_stream_id, NULL); + + CU_ASSERT(0 == rv); + + spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), + &datalen, NGTCP2_WRITE_STREAM_FLAG_FIN, + bidi_stream_id, null_data, 1024, ++t); + + CU_ASSERT((ngtcp2_ssize)sizeof(buf) == spktlen); + CU_ASSERT(670 == datalen); + + rv = ngtcp2_conn_open_uni_stream(conn, &uni_stream_id, NULL); + + CU_ASSERT(0 == rv); + + ngtcp2_conn_extend_max_offset(conn, 1000); + ngtcp2_conn_extend_max_streams_bidi(conn, 7); + ngtcp2_conn_extend_max_streams_uni(conn, 5); + + spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), + &datalen, NGTCP2_WRITE_STREAM_FLAG_FIN, + uni_stream_id, null_data, 300, ++t); + + CU_ASSERT(0 < spktlen); + CU_ASSERT(0 < conn->tx.offset); + CU_ASSERT(conn->local.transport_params.initial_max_data + 1000 == + conn->rx.unsent_max_offset); + CU_ASSERT(conn->local.transport_params.initial_max_streams_bidi + 7 == + conn->remote.bidi.unsent_max_streams); + CU_ASSERT(conn->local.transport_params.initial_max_streams_bidi + 7 == + conn->remote.bidi.max_streams); + CU_ASSERT(conn->local.transport_params.initial_max_streams_uni + 5 == + conn->remote.uni.unsent_max_streams); + CU_ASSERT(conn->local.transport_params.initial_max_streams_uni + 5 == + conn->remote.uni.max_streams); + + fr.type = NGTCP2_FRAME_CRYPTO; + fr.crypto.offset = 0; + fr.crypto.datacnt = 1; + fr.crypto.data[0].len = 198; + fr.crypto.data[0].base = null_data; + + pktlen = write_initial_pkt(buf, sizeof(buf), &conn->oscid, + ngtcp2_conn_get_dcid(conn), 0, NGTCP2_PROTO_VER_V1, + NULL, 0, &fr, 1, &null_ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + rv = ngtcp2_conn_install_rx_handshake_key(conn, &aead_ctx, null_iv, + sizeof(null_iv), &hp_ctx); + + CU_ASSERT(0 == rv); + + rv = ngtcp2_conn_install_tx_handshake_key(conn, &aead_ctx, null_iv, + sizeof(null_iv), &hp_ctx); + + CU_ASSERT(0 == rv); + + rv = ngtcp2_conn_install_rx_key(conn, null_secret, sizeof(null_secret), + &aead_ctx, null_iv, sizeof(null_iv), &hp_ctx); + + CU_ASSERT(0 == rv); + + /* Stream limits in transport parameters can be reduced if early + data is rejected. */ + memset(¶ms, 0, sizeof(params)); + ngtcp2_cid_init(¶ms.initial_scid, conn->dcid.current.cid.data, + conn->dcid.current.cid.datalen); + ngtcp2_cid_init(¶ms.original_dcid, conn->rcid.data, conn->rcid.datalen); + params.max_udp_payload_size = 1200; + params.initial_max_stream_data_bidi_local = + conn->early.transport_params.initial_max_stream_data_bidi_local; + params.initial_max_stream_data_bidi_remote = + conn->early.transport_params.initial_max_stream_data_bidi_remote / 2; + params.initial_max_stream_data_uni = 0; + params.initial_max_data = conn->early.transport_params.initial_max_data; + params.initial_max_streams_bidi = + conn->early.transport_params.initial_max_streams_bidi; + params.initial_max_streams_uni = + conn->early.transport_params.initial_max_streams_uni; + params.active_connection_id_limit = + conn->early.transport_params.active_connection_id_limit; + + rv = ngtcp2_conn_set_remote_transport_params(conn, ¶ms); + + CU_ASSERT(0 == rv); + + rv = ngtcp2_conn_install_tx_key(conn, null_secret, sizeof(null_secret), + &aead_ctx, null_iv, sizeof(null_iv), &hp_ctx); + + CU_ASSERT(0 == rv); + + ngtcp2_conn_handshake_completed(conn); + ngtcp2_conn_early_data_rejected(conn); + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(0 < spktlen); + CU_ASSERT(NULL == ngtcp2_conn_find_stream(conn, bidi_stream_id)); + CU_ASSERT(NULL == ngtcp2_conn_find_stream(conn, uni_stream_id)); + CU_ASSERT(0 == conn->tx.offset); + CU_ASSERT(conn->local.transport_params.initial_max_data == + conn->rx.max_offset); + CU_ASSERT(conn->local.transport_params.initial_max_data == + conn->rx.unsent_max_offset); + CU_ASSERT(conn->local.transport_params.initial_max_streams_bidi == + conn->remote.bidi.max_streams); + CU_ASSERT(conn->local.transport_params.initial_max_streams_bidi == + conn->remote.bidi.unsent_max_streams); + CU_ASSERT(conn->local.transport_params.initial_max_streams_uni == + conn->remote.uni.max_streams); + CU_ASSERT(conn->local.transport_params.initial_max_streams_uni == + conn->remote.uni.unsent_max_streams); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_keep_alive(void) { + ngtcp2_conn *conn; + uint8_t buf[2048]; + ngtcp2_ssize spktlen; + ngtcp2_pkt_info pi; + ngtcp2_tstamp t = 0; + int rv; + + setup_default_client(&conn); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, &pi, buf, sizeof(buf), ++t); + + CU_ASSERT(0 < spktlen); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, &pi, buf, sizeof(buf), ++t); + + CU_ASSERT(0 == spktlen); + + ngtcp2_conn_set_keep_alive_timeout(conn, 10 * NGTCP2_SECONDS); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, &pi, buf, sizeof(buf), t); + + CU_ASSERT(0 == spktlen); + + t += 10 * NGTCP2_SECONDS; + + rv = ngtcp2_conn_handle_expiry(conn, t); + + CU_ASSERT(0 == rv); + CU_ASSERT(conn->flags & NGTCP2_CONN_FLAG_KEEP_ALIVE_CANCELLED); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, &pi, buf, sizeof(buf), t); + + CU_ASSERT(0 < spktlen); + CU_ASSERT(t == conn->keep_alive.last_ts); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_retire_stale_bound_dcid(void) { + ngtcp2_conn *conn; + uint8_t buf[2048]; + size_t pktlen; + ngtcp2_tstamp t = 0; + ngtcp2_tstamp expiry; + int64_t pkt_num = 0; + ngtcp2_frame fr; + int rv; + ngtcp2_cid cid; + const uint8_t raw_cid[] = {0x0f, 0x00, 0x00, 0x00}; + const uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN] = {0xff}; + const uint8_t data[] = {0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8}; + + ngtcp2_cid_init(&cid, raw_cid, sizeof(raw_cid)); + + setup_default_server(&conn); + + fr.type = NGTCP2_FRAME_NEW_CONNECTION_ID; + fr.new_connection_id.seq = 1; + fr.new_connection_id.retire_prior_to = 0; + fr.new_connection_id.cid = cid; + memcpy(fr.new_connection_id.stateless_reset_token, token, sizeof(token)); + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + fr.type = NGTCP2_FRAME_PATH_CHALLENGE; + memcpy(fr.path_challenge.data, data, sizeof(fr.path_challenge.data)); + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &new_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(ngtcp2_ringbuf_len(&conn->rx.path_challenge.rb) > 0); + CU_ASSERT(ngtcp2_ringbuf_len(&conn->dcid.bound.rb) > 0); + + expiry = ngtcp2_conn_get_expiry(conn); + + CU_ASSERT(UINT64_MAX != expiry); + + t += 3 * ngtcp2_conn_get_pto(conn); + + rv = ngtcp2_conn_handle_expiry(conn, t); + + CU_ASSERT(0 == rv); + CU_ASSERT(0 == ngtcp2_ringbuf_len(&conn->dcid.bound.rb)); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_get_scid(void) { + ngtcp2_conn *conn; + ngtcp2_settings settings; + ngtcp2_transport_params params; + ngtcp2_cid dcid, scid; + ngtcp2_callbacks cb; + const uint8_t raw_cid[] = {0x0f, 0x00, 0x00, 0x00}; + ngtcp2_cid scids[16]; + + dcid_init(&dcid); + dcid_init(&scid); + + server_default_callbacks(&cb); + server_default_settings(&settings); + + /* Without preferred address */ + server_default_transport_params(¶ms); + + ngtcp2_conn_server_new(&conn, &dcid, &scid, &null_path.path, + NGTCP2_PROTO_VER_V1, &cb, &settings, ¶ms, + /* mem = */ NULL, NULL); + + CU_ASSERT(1 == ngtcp2_conn_get_num_scid(conn)); + + ngtcp2_conn_get_scid(conn, scids); + + CU_ASSERT(ngtcp2_cid_eq(&scid, &scids[0])); + + ngtcp2_conn_del(conn); + + /* With preferred address */ + server_default_transport_params(¶ms); + params.preferred_address_present = 1; + ngtcp2_cid_init(¶ms.preferred_address.cid, raw_cid, sizeof(raw_cid)); + + ngtcp2_conn_server_new(&conn, &dcid, &scid, &null_path.path, + NGTCP2_PROTO_VER_V1, &cb, &settings, ¶ms, + /* mem = */ NULL, NULL); + + CU_ASSERT(2 == ngtcp2_conn_get_num_scid(conn)); + + ngtcp2_conn_get_scid(conn, scids); + + CU_ASSERT(ngtcp2_cid_eq(&scid, &scids[0])); + CU_ASSERT(ngtcp2_cid_eq(¶ms.preferred_address.cid, &scids[1])); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_stream_close(void) { + ngtcp2_conn *conn; + int rv; + uint8_t buf[2048]; + ngtcp2_frame frs[2]; + size_t pktlen; + int64_t pkt_num = 0; + my_user_data ud; + ngtcp2_strm *strm; + ngtcp2_tstamp t = 0; + ngtcp2_ssize spktlen; + int64_t stream_id; + + /* Receive RESET_STREAM and STOP_SENDING from client */ + setup_default_server(&conn); + conn->callbacks.stream_close = stream_close; + conn->user_data = &ud; + + open_stream(conn, 0); + + frs[0].type = NGTCP2_FRAME_RESET_STREAM; + frs[0].reset_stream.stream_id = 0; + frs[0].reset_stream.app_error_code = NGTCP2_APP_ERR01; + frs[0].reset_stream.final_size = 999; + + frs[1].type = NGTCP2_FRAME_STOP_SENDING; + frs[1].stop_sending.stream_id = 0; + frs[1].stop_sending.app_error_code = NGTCP2_APP_ERR02; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 2, + conn->pktns.crypto.tx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + strm = ngtcp2_conn_find_stream(conn, 0); + + CU_ASSERT(NGTCP2_APP_ERR01 == strm->app_error_code); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + CU_ASSERT((size_t)spktlen < sizeof(buf)); + + frs[0].type = NGTCP2_FRAME_ACK; + frs[0].ack.largest_ack = 0; + frs[0].ack.ack_delay = 0; + frs[0].ack.first_ack_range = 0; + frs[0].ack.rangecnt = 0; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 1, + conn->pktns.crypto.tx.ckm); + + ud.stream_close.flags = NGTCP2_STREAM_CLOSE_FLAG_NONE; + ud.stream_close.stream_id = -1; + ud.stream_close.app_error_code = 0; + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + CU_ASSERT(NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET & + ud.stream_close.flags); + CU_ASSERT(0 == ud.stream_close.stream_id); + CU_ASSERT(NGTCP2_APP_ERR01 == ud.stream_close.app_error_code); + + ngtcp2_conn_del(conn); + + /* Client sends STOP_SENDING and then STREAM and fin */ + pkt_num = 0; + + setup_default_server(&conn); + conn->callbacks.stream_close = stream_close; + conn->callbacks.recv_stream_data = recv_stream_data; + conn->user_data = &ud; + + frs[0].type = NGTCP2_FRAME_STOP_SENDING; + frs[0].stop_sending.stream_id = 0; + frs[0].stop_sending.app_error_code = NGTCP2_APP_ERR01; + + frs[1].type = NGTCP2_FRAME_STREAM; + frs[1].stream.flags = 0; + frs[1].stream.fin = 1; + frs[1].stream.stream_id = 0; + frs[1].stream.offset = 0; + frs[1].stream.datacnt = 0; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 2, + conn->pktns.crypto.tx.ckm); + + ud.stream_data.stream_id = -1; + ud.stream_data.flags = NGTCP2_STREAM_DATA_FLAG_NONE; + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(0 == ud.stream_data.stream_id); + CU_ASSERT((ud.stream_data.flags & NGTCP2_STREAM_DATA_FLAG_FIN) != 0); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + CU_ASSERT((size_t)spktlen < sizeof(buf)); + + frs[0].type = NGTCP2_FRAME_ACK; + frs[0].ack.largest_ack = 0; + frs[0].ack.ack_delay = 0; + frs[0].ack.first_ack_range = 0; + frs[0].ack.rangecnt = 0; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 1, + conn->pktns.crypto.tx.ckm); + + ud.stream_close.flags = NGTCP2_STREAM_CLOSE_FLAG_NONE; + ud.stream_close.stream_id = -1; + ud.stream_close.app_error_code = 0; + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + CU_ASSERT(NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET & + ud.stream_close.flags); + CU_ASSERT(0 == ud.stream_close.stream_id); + CU_ASSERT(NGTCP2_APP_ERR01 == ud.stream_close.app_error_code); + + ngtcp2_conn_del(conn); + + /* Client calls ngtcp2_conn_shutdown_stream, and before sending + STOP_SENDING, it receives STREAM with fin bit set. */ + pkt_num = 0; + + setup_default_client(&conn); + conn->callbacks.stream_close = stream_close; + conn->user_data = &ud; + + rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + + CU_ASSERT(0 == rv); + + spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL, + NGTCP2_WRITE_STREAM_FLAG_FIN, stream_id, + null_data, 1, ++t); + + CU_ASSERT(spktlen > 0); + + frs[0].type = NGTCP2_FRAME_ACK; + frs[0].ack.largest_ack = conn->pktns.tx.last_pkt_num; + frs[0].ack.ack_delay = 0; + frs[0].ack.first_ack_range = 0; + frs[0].ack.rangecnt = 0; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 1, + conn->pktns.crypto.tx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + rv = ngtcp2_conn_shutdown_stream(conn, stream_id, NGTCP2_APP_ERR01); + + CU_ASSERT(0 == rv); + + frs[0].type = NGTCP2_FRAME_STREAM; + frs[0].stream.flags = 0; + frs[0].stream.fin = 1; + frs[0].stream.stream_id = stream_id; + frs[0].stream.offset = 0; + frs[0].stream.datacnt = 1; + frs[0].stream.data[0].len = 97; + frs[0].stream.data[0].base = null_data; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 1, + conn->pktns.crypto.tx.ckm); + + ud.stream_close.flags = NGTCP2_STREAM_CLOSE_FLAG_NONE; + ud.stream_close.stream_id = -1; + ud.stream_close.app_error_code = 0; + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET & + ud.stream_close.flags); + CU_ASSERT(stream_id == ud.stream_close.stream_id); + CU_ASSERT(NGTCP2_APP_ERR01 == ud.stream_close.app_error_code); + + ngtcp2_conn_del(conn); + + /* Client sends STREAM fin and then RESET_STREAM. It receives ACK + for the STREAM frame, then response fin. No ACK for + RESET_STREAM. */ + pkt_num = 0; + + setup_default_client(&conn); + conn->callbacks.stream_close = stream_close; + conn->user_data = &ud; + + rv = ngtcp2_conn_open_bidi_stream(conn, &stream_id, NULL); + + CU_ASSERT(0 == rv); + + spktlen = ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL, + NGTCP2_WRITE_STREAM_FLAG_FIN, stream_id, + null_data, 1, ++t); + + CU_ASSERT(spktlen > 0); + + rv = ngtcp2_conn_shutdown_stream_write(conn, stream_id, NGTCP2_APP_ERR01); + + CU_ASSERT(0 == rv); + + frs[0].type = NGTCP2_FRAME_STREAM; + frs[0].stream.flags = 0; + frs[0].stream.fin = 1; + frs[0].stream.stream_id = stream_id; + frs[0].stream.offset = 0; + frs[0].stream.datacnt = 0; + + frs[1].type = NGTCP2_FRAME_ACK; + frs[1].ack.largest_ack = conn->pktns.tx.last_pkt_num; + frs[1].ack.ack_delay = 0; + frs[1].ack.first_ack_range = 0; + frs[1].ack.rangecnt = 0; + + spktlen = + ngtcp2_conn_write_stream(conn, NULL, NULL, buf, sizeof(buf), NULL, + NGTCP2_WRITE_STREAM_FLAG_NONE, -1, NULL, 0, ++t); + + CU_ASSERT(spktlen > 0); + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, 2, + conn->pktns.crypto.tx.ckm); + + ud.stream_close.flags = NGTCP2_STREAM_CLOSE_FLAG_NONE; + ud.stream_close.stream_id = -1; + ud.stream_close.app_error_code = 0; + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(NGTCP2_STREAM_CLOSE_FLAG_APP_ERROR_CODE_SET & + ud.stream_close.flags); + CU_ASSERT(stream_id == ud.stream_close.stream_id); + CU_ASSERT(NGTCP2_APP_ERR01 == ud.stream_close.app_error_code); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_buffer_pkt(void) { + ngtcp2_conn *conn; + int rv; + uint8_t buf[2048]; + ngtcp2_frame fr; + ngtcp2_frame frs[2]; + size_t pktlen, in_pktlen; + int64_t pkt_num = 0; + ngtcp2_tstamp t = 0; + ngtcp2_ssize spktlen; + ngtcp2_crypto_aead_ctx aead_ctx = {0}; + ngtcp2_crypto_cipher_ctx hp_ctx = {0}; + ngtcp2_ksl_it it; + ngtcp2_pkt_chain *pc; + + /* Server should buffer Short packet if it does not complete + handshake even if it has application tx key. */ + setup_handshake_server(&conn); + + fr.type = NGTCP2_FRAME_CRYPTO; + fr.crypto.offset = 0; + fr.crypto.datacnt = 1; + fr.crypto.data[0].len = 1193; + fr.crypto.data[0].base = null_data; + + pktlen = write_initial_pkt(buf, sizeof(buf), &conn->oscid, + ngtcp2_conn_get_dcid(conn), pkt_num++, + NGTCP2_PROTO_VER_V1, NULL, 0, &fr, 1, &null_ckm); + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + rv = ngtcp2_conn_install_tx_key(conn, null_secret, sizeof(null_secret), + &aead_ctx, null_iv, sizeof(null_iv), &hp_ctx); + + assert(0 == rv); + + rv = ngtcp2_conn_install_rx_key(conn, null_secret, sizeof(null_secret), + &aead_ctx, null_iv, sizeof(null_iv), &hp_ctx); + + assert(0 == rv); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + + fr.type = NGTCP2_FRAME_PING; + + in_pktlen = write_initial_pkt( + buf, sizeof(buf), &conn->oscid, ngtcp2_conn_get_dcid(conn), pkt_num++, + NGTCP2_PROTO_VER_V1, NULL, 0, &fr, 1, &null_ckm); + + frs[0].type = NGTCP2_FRAME_PING; + frs[1].type = NGTCP2_FRAME_PADDING; + frs[1].padding.len = 1200; + + pktlen = write_pkt(buf + in_pktlen, sizeof(buf) - in_pktlen, &conn->oscid, + pkt_num++, frs, 2, &null_ckm); + + CU_ASSERT(!conn->pktns.rx.buffed_pkts); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, + in_pktlen + pktlen, ++t); + + CU_ASSERT(0 == rv); + + pc = conn->pktns.rx.buffed_pkts; + + CU_ASSERT(pktlen == pc->pktlen); + CU_ASSERT(in_pktlen + pktlen == pc->dgramlen); + + it = ngtcp2_acktr_get(&conn->pktns.acktr); + + CU_ASSERT(ngtcp2_ksl_it_end(&it)); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_handshake_timeout(void) { + ngtcp2_conn *conn; + int rv; + + /* handshake has just timed out */ + setup_handshake_server(&conn); + + rv = ngtcp2_conn_handle_expiry(conn, + conn->local.settings.initial_ts + + conn->local.settings.handshake_timeout); + + CU_ASSERT(NGTCP2_ERR_HANDSHAKE_TIMEOUT == rv); + + ngtcp2_conn_del(conn); + + /* handshake is still in progress */ + setup_handshake_server(&conn); + + rv = ngtcp2_conn_handle_expiry( + conn, conn->local.settings.initial_ts + + conn->local.settings.handshake_timeout - 1); + + CU_ASSERT(0 == rv); + + ngtcp2_conn_del(conn); + + /* handshake timeout should be ignored after handshake has + completed. */ + setup_default_server(&conn); + + rv = ngtcp2_conn_handle_expiry(conn, + conn->local.settings.initial_ts + + conn->local.settings.handshake_timeout); + + CU_ASSERT(0 == rv); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_get_connection_close_error(void) { + ngtcp2_conn *conn; + uint8_t buf[2048]; + ngtcp2_frame frs[2]; + size_t pktlen; + uint8_t reason[2048]; + ngtcp2_tstamp t = 0; + int64_t pkt_num = 0; + int rv; + ngtcp2_connection_close_error ccerr; + + memset(reason, 'a', sizeof(reason)); + + setup_default_server(&conn); + + /* Record the last error. */ + frs[0].type = NGTCP2_FRAME_CONNECTION_CLOSE_APP; + frs[0].connection_close.error_code = 1; + frs[0].connection_close.frame_type = 99; + frs[0].connection_close.reasonlen = 10; + frs[0].connection_close.reason = reason; + + frs[1].type = NGTCP2_FRAME_CONNECTION_CLOSE; + frs[1].connection_close.error_code = NGTCP2_PROTOCOL_VIOLATION; + frs[1].connection_close.frame_type = 1000000007; + frs[1].connection_close.reasonlen = + NGTCP2_CONNECTION_CLOSE_ERROR_MAX_REASONLEN + 1; + frs[1].connection_close.reason = reason; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, ++pkt_num, frs, + ngtcp2_arraylen(frs), conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(NGTCP2_ERR_DRAINING == rv); + + ngtcp2_conn_get_connection_close_error(conn, &ccerr); + + CU_ASSERT(NGTCP2_PROTOCOL_VIOLATION == ccerr.error_code); + CU_ASSERT(NGTCP2_CONNECTION_CLOSE_ERROR_CODE_TYPE_TRANSPORT == ccerr.type); + CU_ASSERT(1000000007 == ccerr.frame_type); + CU_ASSERT(0 == memcmp(reason, ccerr.reason, ccerr.reasonlen)); + CU_ASSERT(NGTCP2_CONNECTION_CLOSE_ERROR_MAX_REASONLEN == ccerr.reasonlen); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_version_negotiation(void) { + ngtcp2_conn *conn; + uint8_t buf[2048]; + ngtcp2_frame fr; + ngtcp2_tstamp t = 0; + ngtcp2_ssize spktlen; + int64_t pkt_num = 0; + size_t pktlen; + int rv; + ngtcp2_transport_params remote_params; + uint8_t other_versions[sizeof(uint32_t) * 2]; + uint32_t version; + + ngtcp2_put_uint32be(&other_versions[0], NGTCP2_PROTO_VER_V1); + ngtcp2_put_uint32be(&other_versions[4], NGTCP2_PROTO_VER_V2_DRAFT); + + /* Client sees the change version in Initial packet which contains + CRYPTO frame. It generates new Initial keys and sets negotiated + version. */ + setup_handshake_client(&conn); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + + fr.type = NGTCP2_FRAME_CRYPTO; + fr.crypto.offset = 0; + fr.crypto.datacnt = 1; + fr.crypto.data[0].len = 133; + fr.crypto.data[0].base = null_data; + + pktlen = write_initial_pkt( + buf, sizeof(buf), &conn->oscid, ngtcp2_conn_get_dcid(conn), pkt_num++, + NGTCP2_PROTO_VER_V2_DRAFT, NULL, 0, &fr, 1, &null_ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(NGTCP2_PROTO_VER_V2_DRAFT == conn->negotiated_version); + CU_ASSERT(NGTCP2_PROTO_VER_V2_DRAFT == conn->vneg.version); + CU_ASSERT(conn->vneg.rx.ckm != NULL); + CU_ASSERT(conn->vneg.tx.ckm != NULL); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + + ngtcp2_get_uint32(&version, &buf[1]); + + CU_ASSERT(NGTCP2_PROTO_VER_V2_DRAFT == version); + + ngtcp2_conn_del(conn); + + /* Client receives Initial packet which does not change version and + does not contain CRYPTO frame. It leaves negotiated version + unchanged. */ + setup_handshake_client(&conn); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + + fr.type = NGTCP2_FRAME_PADDING; + fr.padding.len = 1; + + pktlen = write_initial_pkt(buf, sizeof(buf), &conn->oscid, + ngtcp2_conn_get_dcid(conn), pkt_num++, + NGTCP2_PROTO_VER_V1, NULL, 0, &fr, 1, &null_ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(0 == conn->negotiated_version); + CU_ASSERT(0 == conn->vneg.version); + CU_ASSERT(conn->vneg.rx.ckm == NULL); + CU_ASSERT(conn->vneg.tx.ckm == NULL); + + ngtcp2_conn_del(conn); + + /* Server sees client supports QUIC v2. It chooses QUIC v2 as the + negotiated version, and generates new Initial keys. */ + setup_handshake_server(&conn); + + conn->callbacks.recv_client_initial = + recv_client_initial_no_remote_transport_params; + + fr.type = NGTCP2_FRAME_CRYPTO; + fr.crypto.offset = 0; + fr.crypto.datacnt = 1; + fr.crypto.data[0].len = 1233; + fr.crypto.data[0].base = null_data; + + pktlen = write_initial_pkt(buf, sizeof(buf), &conn->oscid, + ngtcp2_conn_get_dcid(conn), pkt_num++, + NGTCP2_PROTO_VER_V1, NULL, 0, &fr, 1, &null_ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + ngtcp2_transport_params_default(&remote_params); + ngtcp2_cid_init(&remote_params.initial_scid, conn->dcid.current.cid.data, + conn->dcid.current.cid.datalen); + remote_params.version_info_present = 1; + remote_params.version_info.chosen_version = NGTCP2_PROTO_VER_V1; + remote_params.version_info.other_versions = other_versions; + remote_params.version_info.other_versionslen = sizeof(other_versions); + + rv = ngtcp2_conn_set_remote_transport_params(conn, &remote_params); + + CU_ASSERT(0 == rv); + CU_ASSERT(NGTCP2_PROTO_VER_V2_DRAFT == conn->negotiated_version); + CU_ASSERT(NGTCP2_PROTO_VER_V2_DRAFT == conn->vneg.version); + CU_ASSERT(conn->vneg.rx.ckm != NULL); + CU_ASSERT(conn->vneg.tx.ckm != NULL); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + + ngtcp2_get_uint32(&version, &buf[1]); + + CU_ASSERT(NGTCP2_PROTO_VER_V2_DRAFT == version); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_server_negotiate_version(void) { + ngtcp2_conn *conn; + ngtcp2_version_info version_info = {0}; + uint8_t client_other_versions[sizeof(uint32_t) * 2]; + + setup_handshake_server(&conn); + + version_info.chosen_version = conn->client_chosen_version; + + /* Empty version_info.other_versions */ + version_info.other_versions = NULL; + version_info.other_versionslen = 0; + + CU_ASSERT(conn->client_chosen_version == + ngtcp2_conn_server_negotiate_version(conn, &version_info)); + + /* version_info.other_versions and preferred_versions do not share + any version. */ + ngtcp2_put_uint32be(&client_other_versions[0], 0xff000001); + ngtcp2_put_uint32be(&client_other_versions[4], 0xff000002); + + version_info.other_versions = client_other_versions; + version_info.other_versionslen = sizeof(uint32_t) * 2; + + CU_ASSERT(conn->client_chosen_version == + ngtcp2_conn_server_negotiate_version(conn, &version_info)); + + /* version_info.other_versions and preferred_versions share the + version. */ + ngtcp2_put_uint32be(&client_other_versions[0], 0xff000001); + ngtcp2_put_uint32be(&client_other_versions[4], NGTCP2_PROTO_VER_V2_DRAFT); + + version_info.other_versions = client_other_versions; + version_info.other_versionslen = sizeof(uint32_t) * 2; + + CU_ASSERT(NGTCP2_PROTO_VER_V2_DRAFT == + ngtcp2_conn_server_negotiate_version(conn, &version_info)); + + ngtcp2_conn_del(conn); + + /* Without preferred_versions */ + setup_handshake_server(&conn); + + ngtcp2_mem_free(conn->mem, conn->vneg.preferred_versions); + conn->vneg.preferred_versions = NULL; + conn->vneg.preferred_versionslen = 0; + + ngtcp2_put_uint32be(&client_other_versions[0], 0xff000001); + ngtcp2_put_uint32be(&client_other_versions[4], NGTCP2_PROTO_VER_V2_DRAFT); + + version_info.other_versions = client_other_versions; + version_info.other_versionslen = sizeof(uint32_t) * 2; + + CU_ASSERT(conn->client_chosen_version == + ngtcp2_conn_server_negotiate_version(conn, &version_info)); + + ngtcp2_conn_del(conn); + + /* original version is the most preferred version */ + setup_handshake_server(&conn); + + conn->vneg.preferred_versions[0] = NGTCP2_PROTO_VER_V1; + conn->vneg.preferred_versions[1] = NGTCP2_PROTO_VER_V2_DRAFT; + + ngtcp2_put_uint32be(&client_other_versions[0], NGTCP2_PROTO_VER_V2_DRAFT); + ngtcp2_put_uint32be(&client_other_versions[4], NGTCP2_PROTO_VER_V1); + + version_info.other_versions = client_other_versions; + version_info.other_versionslen = sizeof(uint32_t) * 2; + + CU_ASSERT(conn->client_chosen_version == + ngtcp2_conn_server_negotiate_version(conn, &version_info)); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_pmtud_loss(void) { + ngtcp2_conn *conn; + uint8_t buf[2048]; + ngtcp2_ssize spktlen; + uint64_t t = 0; + ngtcp2_frame fr; + int64_t pkt_num = 0; + size_t pktlen; + int rv; + + setup_default_client(&conn); + + ngtcp2_conn_start_pmtud(conn); + + /* This sends PMTUD packet. */ + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(1406 == spktlen); + + t += NGTCP2_SECONDS; + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(spktlen > 0); + + fr.type = NGTCP2_FRAME_ACK; + fr.ack.largest_ack = conn->pktns.tx.last_pkt_num; + fr.ack.ack_delay = 0; + fr.ack.first_ack_range = 0; + fr.ack.rangecnt = 0; + + pktlen = write_pkt(buf, sizeof(buf), &conn->oscid, pkt_num++, &fr, 1, + conn->pktns.crypto.rx.ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + CU_ASSERT(1 == conn->pktns.rtb.num_lost_pkts); + CU_ASSERT(1 == conn->pktns.rtb.num_lost_pmtud_pkts); + CU_ASSERT(0 == conn->pktns.rtb.cc_bytes_in_flight); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_conn_amplification(void) { + ngtcp2_conn *conn; + ngtcp2_frame fr; + size_t pktlen; + uint8_t buf[2048]; + ngtcp2_cid rcid; + int64_t pkt_num = 0; + ngtcp2_tstamp t = 0; + ngtcp2_ssize spktlen; + int rv; + + rcid_init(&rcid); + + /* ACK only frame should not be sent due to amplification limit. */ + setup_early_server(&conn); + + fr.type = NGTCP2_FRAME_CRYPTO; + fr.crypto.offset = 0; + fr.crypto.datacnt = 1; + fr.crypto.data[0].len = 1200; + fr.crypto.data[0].base = null_data; + + pktlen = write_initial_pkt( + buf, sizeof(buf), &rcid, ngtcp2_conn_get_dcid(conn), ++pkt_num, + conn->client_chosen_version, NULL, 0, &fr, 1, &null_ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.stream_id = 4; + fr.stream.fin = 0; + fr.stream.offset = 0; + fr.stream.datacnt = 1; + fr.stream.data[0].len = 111; + fr.stream.data[0].base = null_data; + + pktlen = + write_0rtt_pkt(buf, sizeof(buf), &rcid, ngtcp2_conn_get_dcid(conn), + ++pkt_num, conn->client_chosen_version, &fr, 1, &null_ckm); + + rv = ngtcp2_conn_read_pkt(conn, &null_path.path, &null_pi, buf, pktlen, ++t); + + CU_ASSERT(0 == rv); + + /* Adjust condition so that the execution path goes into sending ACK + only frame. */ + conn->dcid.current.bytes_sent = conn->dcid.current.bytes_recv * 3 - 1; + conn->cstat.bytes_in_flight = conn->cstat.cwnd; + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, NULL, buf, sizeof(buf), ++t); + + CU_ASSERT(0 == spktlen); + + ngtcp2_conn_del(conn); +} + +typedef struct failmalloc { + size_t nmalloc; + size_t fail_start; +} failmalloc; + +static void *failmalloc_malloc(size_t size, void *user_data) { + failmalloc *mc = user_data; + + if (mc->fail_start <= ++mc->nmalloc) { + return NULL; + } + + return malloc(size); +} + +static void failmalloc_free(void *ptr, void *user_data) { + (void)user_data; + + free(ptr); +} + +static void *failmalloc_calloc(size_t nmemb, size_t size, void *user_data) { + failmalloc *mc = user_data; + + if (mc->fail_start <= ++mc->nmalloc) { + return NULL; + } + + return calloc(nmemb, size); +} + +static void *failmalloc_realloc(void *ptr, size_t size, void *user_data) { + failmalloc *mc = user_data; + + if (mc->fail_start <= ++mc->nmalloc) { + return NULL; + } + + return realloc(ptr, size); +} + +void test_ngtcp2_conn_new_failmalloc(void) { + ngtcp2_conn *conn; + ngtcp2_callbacks cb; + ngtcp2_settings settings; + ngtcp2_transport_params params; + failmalloc mc; + ngtcp2_mem mem = { + &mc, + failmalloc_malloc, + failmalloc_free, + failmalloc_calloc, + failmalloc_realloc, + }; + ngtcp2_vec token = { + (uint8_t *)"token", + sizeof("token") - 1, + }; + uint32_t preferred_versions[] = { + NGTCP2_PROTO_VER_V1, + NGTCP2_PROTO_VER_V2_DRAFT, + }; + uint32_t other_versions[] = { + NGTCP2_PROTO_VER_V2_DRAFT, + NGTCP2_PROTO_VER_V1, + 0x5a9aeaca, + }; + ngtcp2_cid dcid, scid; + int rv; + size_t i; + size_t nmalloc; + + dcid_init(&dcid); + scid_init(&scid); + + ngtcp2_settings_default(&settings); + ngtcp2_transport_params_default(¶ms); + + settings.qlog.write = qlog_write; + settings.token = token; + settings.preferred_versions = preferred_versions; + settings.preferred_versionslen = ngtcp2_arraylen(preferred_versions); + settings.other_versions = other_versions; + settings.other_versionslen = ngtcp2_arraylen(other_versions); + + /* server */ + server_default_callbacks(&cb); + + mc.nmalloc = 0; + mc.fail_start = SIZE_MAX; + + rv = ngtcp2_conn_server_new(&conn, &dcid, &scid, &null_path.path, + NGTCP2_PROTO_VER_V1, &cb, &settings, ¶ms, + &mem, NULL); + + CU_ASSERT(0 == rv); + + ngtcp2_conn_del(conn); + + nmalloc = mc.nmalloc; + + for (i = 0; i <= nmalloc; ++i) { + mc.nmalloc = 0; + mc.fail_start = i; + + rv = ngtcp2_conn_server_new(&conn, &dcid, &scid, &null_path.path, + NGTCP2_PROTO_VER_V1, &cb, &settings, ¶ms, + &mem, NULL); + + CU_ASSERT(NGTCP2_ERR_NOMEM == rv); + } + + mc.nmalloc = 0; + mc.fail_start = nmalloc + 1; + + rv = ngtcp2_conn_server_new(&conn, &dcid, &scid, &null_path.path, + NGTCP2_PROTO_VER_V1, &cb, &settings, ¶ms, + &mem, NULL); + + CU_ASSERT(0 == rv); + + ngtcp2_conn_del(conn); + + /* client */ + client_default_callbacks(&cb); + + mc.nmalloc = 0; + mc.fail_start = SIZE_MAX; + + rv = ngtcp2_conn_client_new(&conn, &dcid, &scid, &null_path.path, + NGTCP2_PROTO_VER_V1, &cb, &settings, ¶ms, + &mem, NULL); + + CU_ASSERT(0 == rv); + + ngtcp2_conn_del(conn); + + nmalloc = mc.nmalloc; + + for (i = 0; i <= nmalloc; ++i) { + mc.nmalloc = 0; + mc.fail_start = i; + + rv = ngtcp2_conn_client_new(&conn, &dcid, &scid, &null_path.path, + NGTCP2_PROTO_VER_V1, &cb, &settings, ¶ms, + &mem, NULL); + + CU_ASSERT(NGTCP2_ERR_NOMEM == rv); + } + + mc.nmalloc = 0; + mc.fail_start = nmalloc + 1; + + rv = ngtcp2_conn_client_new(&conn, &dcid, &scid, &null_path.path, + NGTCP2_PROTO_VER_V1, &cb, &settings, ¶ms, + &mem, NULL); + + CU_ASSERT(0 == rv); + + ngtcp2_conn_del(conn); +} + +void test_ngtcp2_accept(void) { + size_t pktlen; + uint8_t buf[2048]; + ngtcp2_cid dcid, scid; + ngtcp2_frame fr; + int rv; + ngtcp2_pkt_hd hd; + + dcid_init(&dcid); + scid_init(&scid); + + /* Initial packet */ + memset(&hd, 0, sizeof(hd)); + + fr.type = NGTCP2_FRAME_CRYPTO; + fr.crypto.offset = 0; + fr.crypto.datacnt = 1; + fr.crypto.data[0].len = 1200; + fr.crypto.data[0].base = null_data; + + pktlen = write_initial_pkt(buf, sizeof(buf), &dcid, &scid, 0, + NGTCP2_PROTO_VER_V1, NULL, 0, &fr, 1, &null_ckm); + + CU_ASSERT(pktlen >= 1200); + + rv = ngtcp2_accept(&hd, buf, pktlen); + + CU_ASSERT(0 == rv); + CU_ASSERT(ngtcp2_cid_eq(&dcid, &hd.dcid)); + CU_ASSERT(ngtcp2_cid_eq(&scid, &hd.scid)); + CU_ASSERT(0 == hd.token.len); + CU_ASSERT(hd.len > 0); + CU_ASSERT(NGTCP2_PROTO_VER_V1 == hd.version); + CU_ASSERT(NGTCP2_PKT_INITIAL == hd.type); + CU_ASSERT(hd.flags & NGTCP2_PKT_FLAG_LONG_FORM); + + /* 0RTT packet */ + memset(&hd, 0, sizeof(hd)); + + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.flags = 0; + fr.stream.stream_id = 0; + fr.stream.fin = 0; + fr.stream.offset = 0; + fr.stream.datacnt = 1; + fr.stream.data[0].len = 1200; + fr.stream.data[0].base = null_data; + + pktlen = write_0rtt_pkt(buf, sizeof(buf), &dcid, &scid, 1, + NGTCP2_PROTO_VER_V1, &fr, 1, &null_ckm); + + CU_ASSERT(pktlen >= 1200); + + rv = ngtcp2_accept(&hd, buf, pktlen); + + CU_ASSERT(NGTCP2_ERR_RETRY == rv); + CU_ASSERT(ngtcp2_cid_eq(&dcid, &hd.dcid)); + CU_ASSERT(ngtcp2_cid_eq(&scid, &hd.scid)); + CU_ASSERT(0 == hd.token.len); + CU_ASSERT(hd.len > 0); + CU_ASSERT(NGTCP2_PROTO_VER_V1 == hd.version); + CU_ASSERT(NGTCP2_PKT_0RTT == hd.type); + CU_ASSERT(hd.flags & NGTCP2_PKT_FLAG_LONG_FORM); + + /* Unknown version */ + memset(&hd, 0, sizeof(hd)); + + fr.type = NGTCP2_FRAME_CRYPTO; + fr.crypto.offset = 0; + fr.crypto.datacnt = 1; + fr.crypto.data[0].len = 1200; + fr.crypto.data[0].base = null_data; + + pktlen = write_initial_pkt(buf, sizeof(buf), &dcid, &scid, 0, 0x2, NULL, 0, + &fr, 1, &null_ckm); + + CU_ASSERT(pktlen >= 1200); + + rv = ngtcp2_accept(&hd, buf, pktlen); + + /* Unknown version should be filtered out by earlier call of + ngtcp2_pkt_decode_version_cid, that is, only supported versioned + packet should be passed to ngtcp2_accept. */ + CU_ASSERT(NGTCP2_ERR_INVALID_ARGUMENT == rv); + + /* Unknown version and the UDP payload size is less than + NGTCP2_MAX_UDP_PAYLOAD_SIZE. */ + memset(&hd, 0, sizeof(hd)); + + fr.type = NGTCP2_FRAME_CRYPTO; + fr.crypto.offset = 0; + fr.crypto.datacnt = 1; + fr.crypto.data[0].len = 1127; + fr.crypto.data[0].base = null_data; + + pktlen = write_initial_pkt(buf, sizeof(buf), &dcid, &scid, 0, 0x2, NULL, 0, + &fr, 1, &null_ckm); + + CU_ASSERT(1199 == pktlen); + + rv = ngtcp2_accept(&hd, buf, pktlen); + + CU_ASSERT(NGTCP2_ERR_INVALID_ARGUMENT == rv); + + /* Short packet */ + memset(&hd, 0, sizeof(hd)); + + fr.type = NGTCP2_FRAME_CRYPTO; + fr.crypto.offset = 0; + fr.crypto.datacnt = 1; + fr.crypto.data[0].len = 1200; + fr.crypto.data[0].base = null_data; + + pktlen = write_pkt(buf, sizeof(buf), &dcid, 0, &fr, 1, &null_ckm); + + CU_ASSERT(pktlen >= 1200); + + rv = ngtcp2_accept(&hd, buf, pktlen); + + CU_ASSERT(NGTCP2_ERR_INVALID_ARGUMENT == rv); + + /* Unable to decode packet header */ + memset(&hd, 0, sizeof(hd)); + + memset(buf, 0, 4); + buf[0] = NGTCP2_HEADER_FORM_BIT; + + rv = ngtcp2_accept(&hd, buf, 4); + + CU_ASSERT(NGTCP2_ERR_INVALID_ARGUMENT == rv); +} + +void test_ngtcp2_select_version(void) { + CU_ASSERT(0 == ngtcp2_select_version(NULL, 0, NULL, 0)); + + { + uint32_t preferred_versions[] = {NGTCP2_PROTO_VER_V1, + NGTCP2_PROTO_VER_V2_DRAFT}; + uint32_t offered_versions[] = {0x00000004, 0x00000003, + NGTCP2_PROTO_VER_V2_DRAFT}; + + CU_ASSERT(NGTCP2_PROTO_VER_V2_DRAFT == + ngtcp2_select_version( + preferred_versions, ngtcp2_arraylen(preferred_versions), + offered_versions, ngtcp2_arraylen(offered_versions))); + } + + { + uint32_t preferred_versions[] = {NGTCP2_PROTO_VER_V1, + NGTCP2_PROTO_VER_V2_DRAFT}; + uint32_t offered_versions[] = {0x00000004, 0x00000003}; + + CU_ASSERT(0 == ngtcp2_select_version( + preferred_versions, ngtcp2_arraylen(preferred_versions), + offered_versions, ngtcp2_arraylen(offered_versions))); + } +} + +void test_ngtcp2_pkt_write_connection_close(void) { + ngtcp2_ssize spktlen; + uint8_t buf[1200]; + ngtcp2_cid dcid, scid; + ngtcp2_crypto_aead aead = {0, NGTCP2_INITIAL_AEAD_OVERHEAD}; + ngtcp2_crypto_cipher hp_mask = {0}; + ngtcp2_crypto_aead_ctx aead_ctx = {0}; + ngtcp2_crypto_cipher_ctx hp_ctx = {0}; + + dcid_init(&dcid); + scid_init(&scid); + + spktlen = ngtcp2_pkt_write_connection_close( + buf, sizeof(buf), NGTCP2_PROTO_VER_V1, &dcid, &scid, NGTCP2_INVALID_TOKEN, + (const uint8_t *)"foo", 3, null_encrypt, &aead, &aead_ctx, null_iv, + null_hp_mask, &hp_mask, &hp_ctx); + + CU_ASSERT(spktlen > 0); + + spktlen = ngtcp2_pkt_write_connection_close( + buf, 16, NGTCP2_PROTO_VER_V1, &dcid, &scid, NGTCP2_INVALID_TOKEN, NULL, 0, + null_encrypt, &aead, &aead_ctx, null_iv, null_hp_mask, &hp_mask, &hp_ctx); + + CU_ASSERT(NGTCP2_ERR_NOBUF == spktlen); +} diff --git a/tests/ngtcp2_conn_test.h b/tests/ngtcp2_conn_test.h new file mode 100644 index 0000000..b3c2b3a --- /dev/null +++ b/tests/ngtcp2_conn_test.h @@ -0,0 +1,103 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_CONN_TEST_H +#define NGTCP2_CONN_TEST_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +void init_static_path(void); + +void test_ngtcp2_conn_stream_open_close(void); +void test_ngtcp2_conn_stream_rx_flow_control(void); +void test_ngtcp2_conn_stream_rx_flow_control_error(void); +void test_ngtcp2_conn_stream_tx_flow_control(void); +void test_ngtcp2_conn_rx_flow_control(void); +void test_ngtcp2_conn_rx_flow_control_error(void); +void test_ngtcp2_conn_tx_flow_control(void); +void test_ngtcp2_conn_shutdown_stream_write(void); +void test_ngtcp2_conn_recv_reset_stream(void); +void test_ngtcp2_conn_recv_stop_sending(void); +void test_ngtcp2_conn_recv_conn_id_omitted(void); +void test_ngtcp2_conn_short_pkt_type(void); +void test_ngtcp2_conn_recv_stateless_reset(void); +void test_ngtcp2_conn_recv_retry(void); +void test_ngtcp2_conn_recv_delayed_handshake_pkt(void); +void test_ngtcp2_conn_recv_max_streams(void); +void test_ngtcp2_conn_handshake(void); +void test_ngtcp2_conn_handshake_error(void); +void test_ngtcp2_conn_retransmit_protected(void); +void test_ngtcp2_conn_send_max_stream_data(void); +void test_ngtcp2_conn_recv_stream_data(void); +void test_ngtcp2_conn_recv_ping(void); +void test_ngtcp2_conn_recv_max_stream_data(void); +void test_ngtcp2_conn_send_early_data(void); +void test_ngtcp2_conn_recv_early_data(void); +void test_ngtcp2_conn_recv_compound_pkt(void); +void test_ngtcp2_conn_pkt_payloadlen(void); +void test_ngtcp2_conn_writev_stream(void); +void test_ngtcp2_conn_writev_datagram(void); +void test_ngtcp2_conn_recv_datagram(void); +void test_ngtcp2_conn_recv_new_connection_id(void); +void test_ngtcp2_conn_recv_retire_connection_id(void); +void test_ngtcp2_conn_server_path_validation(void); +void test_ngtcp2_conn_client_connection_migration(void); +void test_ngtcp2_conn_recv_path_challenge(void); +void test_ngtcp2_conn_key_update(void); +void test_ngtcp2_conn_crypto_buffer_exceeded(void); +void test_ngtcp2_conn_handshake_probe(void); +void test_ngtcp2_conn_handshake_loss(void); +void test_ngtcp2_conn_recv_client_initial_retry(void); +void test_ngtcp2_conn_recv_client_initial_token(void); +void test_ngtcp2_conn_get_active_dcid(void); +void test_ngtcp2_conn_recv_version_negotiation(void); +void test_ngtcp2_conn_send_initial_token(void); +void test_ngtcp2_conn_set_remote_transport_params(void); +void test_ngtcp2_conn_write_connection_close(void); +void test_ngtcp2_conn_write_application_close(void); +void test_ngtcp2_conn_rtb_reclaim_on_pto(void); +void test_ngtcp2_conn_rtb_reclaim_on_pto_datagram(void); +void test_ngtcp2_conn_validate_ecn(void); +void test_ngtcp2_conn_path_validation(void); +void test_ngtcp2_conn_early_data_sync_stream_data_limit(void); +void test_ngtcp2_conn_early_data_rejected(void); +void test_ngtcp2_conn_keep_alive(void); +void test_ngtcp2_conn_retire_stale_bound_dcid(void); +void test_ngtcp2_conn_get_scid(void); +void test_ngtcp2_conn_stream_close(void); +void test_ngtcp2_conn_buffer_pkt(void); +void test_ngtcp2_conn_handshake_timeout(void); +void test_ngtcp2_conn_get_connection_close_error(void); +void test_ngtcp2_conn_version_negotiation(void); +void test_ngtcp2_conn_server_negotiate_version(void); +void test_ngtcp2_conn_pmtud_loss(void); +void test_ngtcp2_conn_amplification(void); +void test_ngtcp2_conn_new_failmalloc(void); +void test_ngtcp2_accept(void); +void test_ngtcp2_select_version(void); +void test_ngtcp2_pkt_write_connection_close(void); + +#endif /* NGTCP2_CONN_TEST_H */ diff --git a/tests/ngtcp2_conv_test.c b/tests/ngtcp2_conv_test.c new file mode 100644 index 0000000..7b92ec7 --- /dev/null +++ b/tests/ngtcp2_conv_test.c @@ -0,0 +1,430 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_conv_test.h" + +#include <CUnit/CUnit.h> + +#include "ngtcp2_conv.h" +#include "ngtcp2_net.h" +#include "ngtcp2_test_helper.h" + +void test_ngtcp2_get_varint(void) { + uint8_t buf[256]; + const uint8_t *p; + uint64_t n; + int64_t s; + + /* 0 */ + n = 1; + p = ngtcp2_put_uvarint(buf, 0); + + CU_ASSERT(1 == p - buf); + + p = ngtcp2_get_uvarint(&n, buf); + + CU_ASSERT(1 == p - buf); + CU_ASSERT(0 == n); + + /* 63 */ + n = 0; + p = ngtcp2_put_uvarint(buf, 63); + + CU_ASSERT(1 == p - buf); + + p = ngtcp2_get_uvarint(&n, buf); + + CU_ASSERT(1 == p - buf); + CU_ASSERT(63 == n); + + /* 64 */ + n = 0; + p = ngtcp2_put_uvarint(buf, 64); + + CU_ASSERT(2 == p - buf); + + p = ngtcp2_get_uvarint(&n, buf); + + CU_ASSERT(2 == p - buf); + CU_ASSERT(64 == n); + + /* 16383 */ + n = 0; + p = ngtcp2_put_uvarint(buf, 16383); + + CU_ASSERT(2 == p - buf); + + p = ngtcp2_get_uvarint(&n, buf); + + CU_ASSERT(2 == p - buf); + CU_ASSERT(16383 == n); + + /* 16384 */ + n = 0; + p = ngtcp2_put_uvarint(buf, 16384); + + CU_ASSERT(4 == p - buf); + + p = ngtcp2_get_uvarint(&n, buf); + + CU_ASSERT(4 == p - buf); + CU_ASSERT(16384 == n); + + /* 1073741823 */ + n = 0; + p = ngtcp2_put_uvarint(buf, 1073741823); + + CU_ASSERT(4 == p - buf); + + p = ngtcp2_get_uvarint(&n, buf); + + CU_ASSERT(4 == p - buf); + CU_ASSERT(1073741823 == n); + + /* 1073741824 */ + n = 0; + p = ngtcp2_put_uvarint(buf, 1073741824); + + CU_ASSERT(8 == p - buf); + + p = ngtcp2_get_uvarint(&n, buf); + + CU_ASSERT(8 == p - buf); + CU_ASSERT(1073741824 == n); + + /* 4611686018427387903 */ + n = 0; + p = ngtcp2_put_uvarint(buf, 4611686018427387903ULL); + + CU_ASSERT(8 == p - buf); + + p = ngtcp2_get_uvarint(&n, buf); + + CU_ASSERT(8 == p - buf); + CU_ASSERT(4611686018427387903ULL == n); + + /* Check signed version */ + s = 0; + p = ngtcp2_put_uvarint(buf, 4611686018427387903ULL); + + CU_ASSERT(8 == p - buf); + + p = ngtcp2_get_varint(&s, buf); + + CU_ASSERT(8 == p - buf); + CU_ASSERT(4611686018427387903LL == s); +} + +void test_ngtcp2_get_uvarintlen(void) { + uint8_t c; + + c = 0x00; + + CU_ASSERT(1 == ngtcp2_get_uvarintlen(&c)); + + c = 0x40; + + CU_ASSERT(2 == ngtcp2_get_uvarintlen(&c)); + + c = 0x80; + + CU_ASSERT(4 == ngtcp2_get_uvarintlen(&c)); + + c = 0xc0; + + CU_ASSERT(8 == ngtcp2_get_uvarintlen(&c)); +} + +void test_ngtcp2_get_uint64(void) { + uint8_t buf[256]; + const uint8_t *p; + uint64_t n; + + /* 0 */ + n = 1; + p = ngtcp2_put_uint64be(buf, 0); + + CU_ASSERT(sizeof(n) == p - buf); + + p = ngtcp2_get_uint64(&n, buf); + + CU_ASSERT(sizeof(n) == p - buf); + CU_ASSERT(0 == n); + + /* 12345678900 */ + n = 0; + p = ngtcp2_put_uint64be(buf, 12345678900ULL); + + CU_ASSERT(sizeof(n) == p - buf); + + p = ngtcp2_get_uint64(&n, buf); + + CU_ASSERT(sizeof(n) == p - buf); + CU_ASSERT(12345678900ULL == n); + + /* 18446744073709551615 */ + n = 0; + p = ngtcp2_put_uint64be(buf, 18446744073709551615ULL); + + CU_ASSERT(sizeof(n) == p - buf); + + p = ngtcp2_get_uint64(&n, buf); + + CU_ASSERT(sizeof(n) == p - buf); + CU_ASSERT(18446744073709551615ULL == n); +} + +void test_ngtcp2_get_uint48(void) { + uint8_t buf[256]; + const uint8_t *p; + uint64_t n; + + /* 0 */ + n = 1; + p = ngtcp2_put_uint48be(buf, 0); + + CU_ASSERT(6 == p - buf); + + p = ngtcp2_get_uint48(&n, buf); + + CU_ASSERT(6 == p - buf); + CU_ASSERT(0 == n); + + /* 123456789 */ + n = 0; + p = ngtcp2_put_uint48be(buf, 123456789); + + CU_ASSERT(6 == p - buf); + + p = ngtcp2_get_uint48(&n, buf); + + CU_ASSERT(6 == p - buf); + CU_ASSERT(123456789 == n); + + /* 281474976710655 */ + n = 0; + p = ngtcp2_put_uint48be(buf, 281474976710655ULL); + + CU_ASSERT(6 == p - buf); + + p = ngtcp2_get_uint48(&n, buf); + + CU_ASSERT(6 == p - buf); + CU_ASSERT(281474976710655ULL == n); +} + +void test_ngtcp2_get_uint32(void) { + uint8_t buf[256]; + const uint8_t *p; + uint32_t n; + + /* 0 */ + n = 1; + p = ngtcp2_put_uint32be(buf, 0); + + CU_ASSERT(sizeof(n) == p - buf); + + p = ngtcp2_get_uint32(&n, buf); + + CU_ASSERT(sizeof(n) == p - buf); + CU_ASSERT(0 == n); + + /* 123456 */ + n = 0; + p = ngtcp2_put_uint32be(buf, 123456); + + CU_ASSERT(sizeof(n) == p - buf); + + p = ngtcp2_get_uint32(&n, buf); + + CU_ASSERT(sizeof(n) == p - buf); + CU_ASSERT(123456 == n); + + /* 4294967295 */ + n = 0; + p = ngtcp2_put_uint32be(buf, 4294967295UL); + + CU_ASSERT(sizeof(n) == p - buf); + + p = ngtcp2_get_uint32(&n, buf); + + CU_ASSERT(sizeof(n) == p - buf); + CU_ASSERT(4294967295UL == n); +} + +void test_ngtcp2_get_uint24(void) { + uint8_t buf[256]; + const uint8_t *p; + uint32_t n; + + /* 0 */ + n = 1; + p = ngtcp2_put_uint24be(buf, 0); + + CU_ASSERT(3 == p - buf); + + p = ngtcp2_get_uint24(&n, buf); + + CU_ASSERT(3 == p - buf); + CU_ASSERT(0 == n); + + /* 12345 */ + n = 0; + p = ngtcp2_put_uint24be(buf, 12345); + + CU_ASSERT(3 == p - buf); + + p = ngtcp2_get_uint24(&n, buf); + + CU_ASSERT(3 == p - buf); + CU_ASSERT(12345 == n); + + /* 16777215 */ + n = 0; + p = ngtcp2_put_uint24be(buf, 16777215); + + CU_ASSERT(3 == p - buf); + + p = ngtcp2_get_uint24(&n, buf); + + CU_ASSERT(3 == p - buf); + CU_ASSERT(16777215 == n); +} + +void test_ngtcp2_get_uint16(void) { + uint8_t buf[256]; + const uint8_t *p; + uint16_t n; + + /* 0 */ + n = 1; + p = ngtcp2_put_uint16be(buf, 0); + + CU_ASSERT(sizeof(n) == p - buf); + + p = ngtcp2_get_uint16(&n, buf); + + CU_ASSERT(sizeof(n) == p - buf); + CU_ASSERT(0 == n); + + /* 1234 */ + n = 0; + p = ngtcp2_put_uint16be(buf, 1234); + + CU_ASSERT(sizeof(n) == p - buf); + + p = ngtcp2_get_uint16(&n, buf); + + CU_ASSERT(sizeof(n) == p - buf); + CU_ASSERT(1234 == n); + + /* 65535 */ + n = 0; + p = ngtcp2_put_uint16be(buf, 65535); + + CU_ASSERT(sizeof(n) == p - buf); + + p = ngtcp2_get_uint16(&n, buf); + + CU_ASSERT(sizeof(n) == p - buf); + CU_ASSERT(65535 == n); +} + +void test_ngtcp2_get_uint16be(void) { + uint8_t buf[256]; + const uint8_t *p; + uint16_t n; + + /* 0 */ + n = 1; + p = ngtcp2_put_uint16(buf, 0); + + CU_ASSERT(sizeof(n) == p - buf); + + p = ngtcp2_get_uint16be(&n, buf); + + CU_ASSERT(sizeof(n) == p - buf); + CU_ASSERT(0 == n); + + /* 1234 */ + n = 0; + p = ngtcp2_put_uint16(buf, ngtcp2_htons(1234)); + + CU_ASSERT(sizeof(n) == p - buf); + + p = ngtcp2_get_uint16be(&n, buf); + + CU_ASSERT(sizeof(n) == p - buf); + CU_ASSERT(1234 == ngtcp2_ntohs(n)); + + /* 65535 */ + n = 0; + p = ngtcp2_put_uint16(buf, 65535); + + CU_ASSERT(sizeof(n) == p - buf); + + p = ngtcp2_get_uint16be(&n, buf); + + CU_ASSERT(sizeof(n) == p - buf); + CU_ASSERT(65535 == n); +} + +void test_ngtcp2_put_uvarintlen(void) { + CU_ASSERT(1 == ngtcp2_put_uvarintlen(0)); + CU_ASSERT(1 == ngtcp2_put_uvarintlen(63)); + CU_ASSERT(2 == ngtcp2_put_uvarintlen(64)); + CU_ASSERT(2 == ngtcp2_put_uvarintlen(16383)); + CU_ASSERT(4 == ngtcp2_put_uvarintlen(16384)); + CU_ASSERT(4 == ngtcp2_put_uvarintlen(1073741823)); + CU_ASSERT(8 == ngtcp2_put_uvarintlen(1073741824)); + CU_ASSERT(8 == ngtcp2_put_uvarintlen(4611686018427387903ULL)); +} + +void test_ngtcp2_nth_server_bidi_id(void) { + CU_ASSERT(0 == ngtcp2_nth_server_bidi_id(0)); + CU_ASSERT(1 == ngtcp2_nth_server_bidi_id(1)); + CU_ASSERT(5 == ngtcp2_nth_server_bidi_id(2)); + CU_ASSERT(9 == ngtcp2_nth_server_bidi_id(3)); +} + +void test_ngtcp2_nth_server_uni_id(void) { + CU_ASSERT(0 == ngtcp2_nth_server_uni_id(0)); + CU_ASSERT(3 == ngtcp2_nth_server_uni_id(1)); + CU_ASSERT(7 == ngtcp2_nth_server_uni_id(2)); + CU_ASSERT(11 == ngtcp2_nth_server_uni_id(3)); +} + +void test_ngtcp2_nth_client_bidi_id(void) { + CU_ASSERT(0 == ngtcp2_nth_client_bidi_id(0)); + CU_ASSERT(0 == ngtcp2_nth_client_bidi_id(1)); + CU_ASSERT(4 == ngtcp2_nth_client_bidi_id(2)); + CU_ASSERT(8 == ngtcp2_nth_client_bidi_id(3)); +} + +void test_ngtcp2_nth_client_uni_id(void) { + CU_ASSERT(0 == ngtcp2_nth_client_uni_id(0)); + CU_ASSERT(2 == ngtcp2_nth_client_uni_id(1)); + CU_ASSERT(6 == ngtcp2_nth_client_uni_id(2)); + CU_ASSERT(10 == ngtcp2_nth_client_uni_id(3)); +} diff --git a/tests/ngtcp2_conv_test.h b/tests/ngtcp2_conv_test.h new file mode 100644 index 0000000..1cd4324 --- /dev/null +++ b/tests/ngtcp2_conv_test.h @@ -0,0 +1,46 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_CONV_TEST_H +#define NGTCP2_CONV_TEST_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +void test_ngtcp2_get_varint(void); +void test_ngtcp2_get_uvarintlen(void); +void test_ngtcp2_put_uvarintlen(void); +void test_ngtcp2_get_uint64(void); +void test_ngtcp2_get_uint48(void); +void test_ngtcp2_get_uint32(void); +void test_ngtcp2_get_uint24(void); +void test_ngtcp2_get_uint16(void); +void test_ngtcp2_get_uint16be(void); +void test_ngtcp2_nth_server_bidi_id(void); +void test_ngtcp2_nth_server_uni_id(void); +void test_ngtcp2_nth_client_bidi_id(void); +void test_ngtcp2_nth_client_uni_id(void); + +#endif /* NGTCP2_CONV_TEST_H */ diff --git a/tests/ngtcp2_crypto_test.c b/tests/ngtcp2_crypto_test.c new file mode 100644 index 0000000..7469968 --- /dev/null +++ b/tests/ngtcp2_crypto_test.c @@ -0,0 +1,665 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_crypto_test.h" + +#include <CUnit/CUnit.h> + +#include "ngtcp2_crypto.h" +#include "ngtcp2_cid.h" +#include "ngtcp2_conv.h" +#include "ngtcp2_net.h" +#include "ngtcp2_test_helper.h" + +static size_t varint_paramlen(ngtcp2_transport_param_id id, uint64_t value) { + size_t valuelen = ngtcp2_put_uvarintlen(value); + return ngtcp2_put_uvarintlen(id) + ngtcp2_put_uvarintlen(valuelen) + valuelen; +} + +void test_ngtcp2_encode_transport_params(void) { + ngtcp2_transport_params params, nparams; + uint8_t buf[512]; + ngtcp2_ssize nwrite; + int rv; + size_t i, len; + ngtcp2_cid rcid, scid, dcid; + uint8_t other_versions[sizeof(uint32_t) * 3]; + ngtcp2_sockaddr_in6 *sa_in6; + + rcid_init(&rcid); + scid_init(&scid); + dcid_init(&dcid); + + memset(¶ms, 0, sizeof(params)); + memset(&nparams, 0, sizeof(nparams)); + + for (i = 0; i < sizeof(other_versions); i += sizeof(uint32_t)) { + ngtcp2_put_uint32be(&other_versions[i], (uint32_t)(0xff000000u + i)); + } + + /* CH, required parameters only */ + params.max_udp_payload_size = NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE; + params.ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT; + params.max_ack_delay = NGTCP2_DEFAULT_MAX_ACK_DELAY; + params.initial_scid = scid; + + len = (ngtcp2_put_uvarintlen( + NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID) + + ngtcp2_put_uvarintlen(params.initial_scid.datalen) + + params.initial_scid.datalen); + + nwrite = ngtcp2_encode_transport_params( + buf, sizeof(buf), NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO, ¶ms); + + CU_ASSERT((ngtcp2_ssize)len == nwrite); + + rv = ngtcp2_decode_transport_params( + &nparams, NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO, buf, (size_t)nwrite); + + CU_ASSERT(0 == rv); + CU_ASSERT(params.initial_max_stream_data_bidi_local == + nparams.initial_max_stream_data_bidi_local); + CU_ASSERT(params.initial_max_stream_data_bidi_remote == + nparams.initial_max_stream_data_bidi_remote); + CU_ASSERT(params.initial_max_stream_data_uni == + nparams.initial_max_stream_data_uni); + CU_ASSERT(params.initial_max_data == nparams.initial_max_data); + CU_ASSERT(params.initial_max_streams_bidi == + nparams.initial_max_streams_bidi); + CU_ASSERT(params.initial_max_streams_uni == nparams.initial_max_streams_uni); + CU_ASSERT(params.max_idle_timeout == nparams.max_idle_timeout); + CU_ASSERT(params.max_udp_payload_size == nparams.max_udp_payload_size); + CU_ASSERT(params.ack_delay_exponent == nparams.ack_delay_exponent); + CU_ASSERT(params.stateless_reset_token_present == + nparams.stateless_reset_token_present); + CU_ASSERT(params.disable_active_migration == + nparams.disable_active_migration); + CU_ASSERT(params.max_ack_delay == nparams.max_ack_delay); + CU_ASSERT(ngtcp2_cid_eq(¶ms.initial_scid, &nparams.initial_scid)); + + memset(¶ms, 0, sizeof(params)); + memset(&nparams, 0, sizeof(nparams)); + + /* EE, required parameters only */ + params.max_udp_payload_size = NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE; + params.ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT; + params.max_ack_delay = NGTCP2_DEFAULT_MAX_ACK_DELAY; + params.original_dcid = dcid; + params.initial_scid = scid; + + len = (ngtcp2_put_uvarintlen( + NGTCP2_TRANSPORT_PARAM_ORIGINAL_DESTINATION_CONNECTION_ID) + + ngtcp2_put_uvarintlen(params.original_dcid.datalen) + + params.original_dcid.datalen) + + (ngtcp2_put_uvarintlen( + NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID) + + ngtcp2_put_uvarintlen(params.initial_scid.datalen) + + params.initial_scid.datalen); + + nwrite = ngtcp2_encode_transport_params( + buf, sizeof(buf), NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS, + ¶ms); + + CU_ASSERT((ngtcp2_ssize)len == nwrite); + + rv = ngtcp2_decode_transport_params( + &nparams, NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS, buf, + (size_t)nwrite); + + CU_ASSERT(0 == rv); + CU_ASSERT(params.initial_max_stream_data_bidi_local == + nparams.initial_max_stream_data_bidi_local); + CU_ASSERT(params.initial_max_stream_data_bidi_remote == + nparams.initial_max_stream_data_bidi_remote); + CU_ASSERT(params.initial_max_stream_data_uni == + nparams.initial_max_stream_data_uni); + CU_ASSERT(params.initial_max_data == nparams.initial_max_data); + CU_ASSERT(params.initial_max_streams_bidi == + nparams.initial_max_streams_bidi); + CU_ASSERT(params.initial_max_streams_uni == nparams.initial_max_streams_uni); + CU_ASSERT(params.max_idle_timeout == nparams.max_idle_timeout); + CU_ASSERT(params.max_udp_payload_size == nparams.max_udp_payload_size); + CU_ASSERT(params.stateless_reset_token_present == + nparams.stateless_reset_token_present); + CU_ASSERT(params.ack_delay_exponent == nparams.ack_delay_exponent); + CU_ASSERT(params.disable_active_migration == + nparams.disable_active_migration); + CU_ASSERT(params.max_ack_delay == nparams.max_ack_delay); + CU_ASSERT(ngtcp2_cid_eq(¶ms.original_dcid, &nparams.original_dcid)); + CU_ASSERT(ngtcp2_cid_eq(¶ms.initial_scid, &nparams.initial_scid)); + CU_ASSERT(params.retry_scid_present == nparams.retry_scid_present); + + memset(¶ms, 0, sizeof(params)); + memset(&nparams, 0, sizeof(nparams)); + + /* CH, all parameters */ + params.initial_max_stream_data_bidi_local = 1000000007; + params.initial_max_stream_data_bidi_remote = 961748941; + params.initial_max_stream_data_uni = 982451653; + params.initial_max_data = 1000000009; + params.initial_max_streams_bidi = 909; + params.initial_max_streams_uni = 911; + params.max_idle_timeout = 1023 * NGTCP2_MILLISECONDS; + params.max_udp_payload_size = 1400; + params.ack_delay_exponent = 20; + params.disable_active_migration = 1; + params.max_ack_delay = 59 * NGTCP2_MILLISECONDS; + params.initial_scid = scid; + params.active_connection_id_limit = 1000000007; + params.max_datagram_frame_size = 65535; + params.grease_quic_bit = 1; + params.version_info.chosen_version = NGTCP2_PROTO_VER_V1; + params.version_info.other_versions = other_versions; + params.version_info.other_versionslen = sizeof(other_versions); + params.version_info_present = 1; + + len = + varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL, + params.initial_max_stream_data_bidi_local) + + varint_paramlen( + NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE, + params.initial_max_stream_data_bidi_remote) + + varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_UNI, + params.initial_max_stream_data_uni) + + varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_DATA, + params.initial_max_data) + + varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_BIDI, + params.initial_max_streams_bidi) + + varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI, + params.initial_max_streams_uni) + + varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT, + params.max_idle_timeout / NGTCP2_MILLISECONDS) + + varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE, + params.max_udp_payload_size) + + varint_paramlen(NGTCP2_TRANSPORT_PARAM_ACK_DELAY_EXPONENT, + params.ack_delay_exponent) + + (ngtcp2_put_uvarintlen(NGTCP2_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION) + + ngtcp2_put_uvarintlen(0)) + + varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_ACK_DELAY, + params.max_ack_delay / NGTCP2_MILLISECONDS) + + varint_paramlen(NGTCP2_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT, + params.active_connection_id_limit) + + (ngtcp2_put_uvarintlen( + NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID) + + ngtcp2_put_uvarintlen(params.initial_scid.datalen) + + params.initial_scid.datalen) + + varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE, + params.max_datagram_frame_size) + + (ngtcp2_put_uvarintlen(NGTCP2_TRANSPORT_PARAM_GREASE_QUIC_BIT) + + ngtcp2_put_uvarintlen(0)) + + (ngtcp2_put_uvarintlen(NGTCP2_TRANSPORT_PARAM_VERSION_INFORMATION_DRAFT) + + ngtcp2_put_uvarintlen(sizeof(params.version_info.chosen_version) + + params.version_info.other_versionslen) + + sizeof(params.version_info.chosen_version) + + params.version_info.other_versionslen); + + nwrite = ngtcp2_encode_transport_params( + NULL, 0, NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO, ¶ms); + + CU_ASSERT((ngtcp2_ssize)len == nwrite); + + for (i = 0; i < len; ++i) { + nwrite = ngtcp2_encode_transport_params( + buf, i, NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO, ¶ms); + CU_ASSERT(NGTCP2_ERR_NOBUF == nwrite); + } + nwrite = ngtcp2_encode_transport_params( + buf, i, NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO, ¶ms); + + CU_ASSERT((ngtcp2_ssize)i == nwrite); + + rv = ngtcp2_decode_transport_params( + &nparams, NGTCP2_TRANSPORT_PARAMS_TYPE_CLIENT_HELLO, buf, (size_t)nwrite); + + CU_ASSERT(0 == rv); + CU_ASSERT(params.initial_max_stream_data_bidi_local == + nparams.initial_max_stream_data_bidi_local); + CU_ASSERT(params.initial_max_stream_data_bidi_remote == + nparams.initial_max_stream_data_bidi_remote); + CU_ASSERT(params.initial_max_stream_data_uni == + nparams.initial_max_stream_data_uni); + CU_ASSERT(params.initial_max_data == nparams.initial_max_data); + CU_ASSERT(params.initial_max_streams_bidi == + nparams.initial_max_streams_bidi); + CU_ASSERT(params.initial_max_streams_uni == nparams.initial_max_streams_uni); + CU_ASSERT(params.max_idle_timeout == nparams.max_idle_timeout); + CU_ASSERT(params.max_udp_payload_size == nparams.max_udp_payload_size); + CU_ASSERT(params.ack_delay_exponent == nparams.ack_delay_exponent); + CU_ASSERT(params.disable_active_migration == + nparams.disable_active_migration); + CU_ASSERT(params.max_ack_delay == nparams.max_ack_delay); + CU_ASSERT(params.active_connection_id_limit == + nparams.active_connection_id_limit); + CU_ASSERT(params.max_datagram_frame_size == nparams.max_datagram_frame_size); + CU_ASSERT(params.grease_quic_bit == nparams.grease_quic_bit); + CU_ASSERT(params.version_info_present == nparams.version_info_present); + CU_ASSERT(params.version_info.chosen_version == + nparams.version_info.chosen_version); + CU_ASSERT(0 == memcmp(params.version_info.other_versions, + nparams.version_info.other_versions, + params.version_info.other_versionslen)); + + memset(¶ms, 0, sizeof(params)); + memset(&nparams, 0, sizeof(nparams)); + + /* EE, all parameters */ + params.initial_max_stream_data_bidi_local = 1000000007; + params.initial_max_stream_data_bidi_remote = 961748941; + params.initial_max_stream_data_uni = 982451653; + params.initial_max_data = 1000000009; + params.initial_max_streams_bidi = 908; + params.initial_max_streams_uni = 16383; + params.max_idle_timeout = 16363 * NGTCP2_MILLISECONDS; + params.max_udp_payload_size = 1200; + params.stateless_reset_token_present = 1; + memset(params.stateless_reset_token, 0xf1, + sizeof(params.stateless_reset_token)); + params.ack_delay_exponent = 20; + params.preferred_address_present = 1; + params.preferred_address.ipv4_present = 0; + sa_in6 = ¶ms.preferred_address.ipv6; + sa_in6->sin6_family = AF_INET6; + memset(&sa_in6->sin6_addr, 0xe1, sizeof(sa_in6->sin6_addr)); + sa_in6->sin6_port = ngtcp2_htons(63111); + params.preferred_address.ipv6_present = 1; + scid_init(¶ms.preferred_address.cid); + memset(params.preferred_address.stateless_reset_token, 0xd1, + sizeof(params.preferred_address.stateless_reset_token)); + params.disable_active_migration = 1; + params.max_ack_delay = 63 * NGTCP2_MILLISECONDS; + params.retry_scid_present = 1; + params.retry_scid = rcid; + params.original_dcid = dcid; + params.initial_scid = scid; + params.active_connection_id_limit = 1073741824; + params.max_datagram_frame_size = 63; + params.grease_quic_bit = 1; + params.version_info.chosen_version = NGTCP2_PROTO_VER_V1; + params.version_info.other_versions = other_versions; + params.version_info.other_versionslen = ngtcp2_arraylen(other_versions); + params.version_info_present = 1; + + len = + varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL, + params.initial_max_stream_data_bidi_local) + + varint_paramlen( + NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE, + params.initial_max_stream_data_bidi_remote) + + varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_UNI, + params.initial_max_stream_data_uni) + + varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_DATA, + params.initial_max_data) + + varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_BIDI, + params.initial_max_streams_bidi) + + varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI, + params.initial_max_streams_uni) + + varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT, + params.max_idle_timeout / NGTCP2_MILLISECONDS) + + varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE, + params.max_udp_payload_size) + + varint_paramlen(NGTCP2_TRANSPORT_PARAM_ACK_DELAY_EXPONENT, + params.ack_delay_exponent) + + (ngtcp2_put_uvarintlen(NGTCP2_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION) + + ngtcp2_put_uvarintlen(0)) + + varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_ACK_DELAY, + params.max_ack_delay / NGTCP2_MILLISECONDS) + + varint_paramlen(NGTCP2_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT, + params.active_connection_id_limit) + + (ngtcp2_put_uvarintlen(NGTCP2_TRANSPORT_PARAM_STATELESS_RESET_TOKEN) + + ngtcp2_put_uvarintlen(NGTCP2_STATELESS_RESET_TOKENLEN) + + NGTCP2_STATELESS_RESET_TOKENLEN) + + (ngtcp2_put_uvarintlen(NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS) + + ngtcp2_put_uvarintlen(4 + 2 + 16 + 2 + 1 + + params.preferred_address.cid.datalen + + NGTCP2_STATELESS_RESET_TOKENLEN) + + 4 + 2 + 16 + 2 + 1 + params.preferred_address.cid.datalen + + NGTCP2_STATELESS_RESET_TOKENLEN) + + (ngtcp2_put_uvarintlen( + NGTCP2_TRANSPORT_PARAM_RETRY_SOURCE_CONNECTION_ID) + + ngtcp2_put_uvarintlen(params.retry_scid.datalen) + + params.retry_scid.datalen) + + (ngtcp2_put_uvarintlen( + NGTCP2_TRANSPORT_PARAM_ORIGINAL_DESTINATION_CONNECTION_ID) + + ngtcp2_put_uvarintlen(params.original_dcid.datalen) + + params.original_dcid.datalen) + + (ngtcp2_put_uvarintlen( + NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID) + + ngtcp2_put_uvarintlen(params.initial_scid.datalen) + + params.initial_scid.datalen) + + varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE, + params.max_datagram_frame_size) + + (ngtcp2_put_uvarintlen(NGTCP2_TRANSPORT_PARAM_GREASE_QUIC_BIT) + + ngtcp2_put_uvarintlen(0)) + + (ngtcp2_put_uvarintlen(NGTCP2_TRANSPORT_PARAM_VERSION_INFORMATION_DRAFT) + + ngtcp2_put_uvarintlen(sizeof(params.version_info.chosen_version) + + params.version_info.other_versionslen) + + sizeof(params.version_info.chosen_version) + + params.version_info.other_versionslen); + + nwrite = ngtcp2_encode_transport_params( + NULL, 0, NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS, ¶ms); + + CU_ASSERT((ngtcp2_ssize)len == nwrite); + + for (i = 0; i < len; ++i) { + nwrite = ngtcp2_encode_transport_params( + buf, i, NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS, ¶ms); + + CU_ASSERT(NGTCP2_ERR_NOBUF == nwrite); + } + nwrite = ngtcp2_encode_transport_params( + buf, i, NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS, ¶ms); + + CU_ASSERT((ngtcp2_ssize)i == nwrite); + + rv = ngtcp2_decode_transport_params( + &nparams, NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS, buf, + (size_t)nwrite); + + CU_ASSERT(0 == rv); + CU_ASSERT(params.initial_max_stream_data_bidi_local == + nparams.initial_max_stream_data_bidi_local); + CU_ASSERT(params.initial_max_stream_data_bidi_remote == + nparams.initial_max_stream_data_bidi_remote); + CU_ASSERT(params.initial_max_stream_data_uni == + nparams.initial_max_stream_data_uni); + CU_ASSERT(params.initial_max_data == nparams.initial_max_data); + CU_ASSERT(params.initial_max_streams_bidi == + nparams.initial_max_streams_bidi); + CU_ASSERT(params.initial_max_streams_uni == nparams.initial_max_streams_uni); + CU_ASSERT(params.max_idle_timeout == nparams.max_idle_timeout); + CU_ASSERT(params.max_udp_payload_size == nparams.max_udp_payload_size); + CU_ASSERT(0 == memcmp(params.stateless_reset_token, + nparams.stateless_reset_token, + sizeof(params.stateless_reset_token))); + CU_ASSERT(params.ack_delay_exponent == nparams.ack_delay_exponent); + CU_ASSERT(params.preferred_address_present == + nparams.preferred_address_present); + CU_ASSERT(0 == memcmp(¶ms.preferred_address.ipv4, + &nparams.preferred_address.ipv4, + sizeof(params.preferred_address.ipv4))); + CU_ASSERT(params.preferred_address.ipv4_present == + nparams.preferred_address.ipv4_present); + CU_ASSERT(0 == memcmp(¶ms.preferred_address.ipv6, + &nparams.preferred_address.ipv6, + sizeof(params.preferred_address.ipv6))); + CU_ASSERT(params.preferred_address.ipv6_present == + nparams.preferred_address.ipv6_present); + CU_ASSERT(ngtcp2_cid_eq(¶ms.preferred_address.cid, + &nparams.preferred_address.cid)); + CU_ASSERT(0 == + memcmp(params.preferred_address.stateless_reset_token, + nparams.preferred_address.stateless_reset_token, + sizeof(params.preferred_address.stateless_reset_token))); + CU_ASSERT(params.disable_active_migration == + nparams.disable_active_migration); + CU_ASSERT(params.max_ack_delay == nparams.max_ack_delay); + CU_ASSERT(params.retry_scid_present == nparams.retry_scid_present); + CU_ASSERT(ngtcp2_cid_eq(¶ms.retry_scid, &nparams.retry_scid)); + CU_ASSERT(ngtcp2_cid_eq(¶ms.initial_scid, &nparams.initial_scid)); + CU_ASSERT(ngtcp2_cid_eq(¶ms.original_dcid, &nparams.original_dcid)); + CU_ASSERT(params.active_connection_id_limit == + nparams.active_connection_id_limit); + CU_ASSERT(params.max_datagram_frame_size == nparams.max_datagram_frame_size); + CU_ASSERT(params.grease_quic_bit = nparams.grease_quic_bit); + CU_ASSERT(params.version_info_present == nparams.version_info_present); + CU_ASSERT(params.version_info.chosen_version == + nparams.version_info.chosen_version); + CU_ASSERT(0 == memcmp(params.version_info.other_versions, + nparams.version_info.other_versions, + params.version_info.other_versionslen)); +} + +void test_ngtcp2_decode_transport_params_new(void) { + ngtcp2_transport_params params, *nparams; + uint8_t buf[512]; + ngtcp2_ssize nwrite; + int rv; + size_t i, len; + ngtcp2_cid rcid, scid, dcid; + uint8_t other_versions[sizeof(uint32_t) * 3]; + ngtcp2_sockaddr_in *sa_in; + + rcid_init(&rcid); + scid_init(&scid); + dcid_init(&dcid); + + memset(¶ms, 0, sizeof(params)); + memset(&nparams, 0, sizeof(nparams)); + + for (i = 0; i < sizeof(other_versions); i += sizeof(uint32_t)) { + ngtcp2_put_uint32be(&other_versions[i], (uint32_t)(0xff000000u + i)); + } + + /* EE, required parameters only */ + params.max_udp_payload_size = NGTCP2_DEFAULT_MAX_RECV_UDP_PAYLOAD_SIZE; + params.ack_delay_exponent = NGTCP2_DEFAULT_ACK_DELAY_EXPONENT; + params.max_ack_delay = NGTCP2_DEFAULT_MAX_ACK_DELAY; + params.original_dcid = dcid; + params.initial_scid = scid; + + len = (ngtcp2_put_uvarintlen( + NGTCP2_TRANSPORT_PARAM_ORIGINAL_DESTINATION_CONNECTION_ID) + + ngtcp2_put_uvarintlen(params.original_dcid.datalen) + + params.original_dcid.datalen) + + (ngtcp2_put_uvarintlen( + NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID) + + ngtcp2_put_uvarintlen(params.initial_scid.datalen) + + params.initial_scid.datalen); + + nwrite = ngtcp2_encode_transport_params( + buf, sizeof(buf), NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS, + ¶ms); + + CU_ASSERT((ngtcp2_ssize)len == nwrite); + + rv = ngtcp2_decode_transport_params_new( + &nparams, NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS, buf, + (size_t)nwrite, NULL); + + CU_ASSERT(0 == rv); + CU_ASSERT(params.initial_max_stream_data_bidi_local == + nparams->initial_max_stream_data_bidi_local); + CU_ASSERT(params.initial_max_stream_data_bidi_remote == + nparams->initial_max_stream_data_bidi_remote); + CU_ASSERT(params.initial_max_stream_data_uni == + nparams->initial_max_stream_data_uni); + CU_ASSERT(params.initial_max_data == nparams->initial_max_data); + CU_ASSERT(params.initial_max_streams_bidi == + nparams->initial_max_streams_bidi); + CU_ASSERT(params.initial_max_streams_uni == nparams->initial_max_streams_uni); + CU_ASSERT(params.max_idle_timeout == nparams->max_idle_timeout); + CU_ASSERT(params.max_udp_payload_size == nparams->max_udp_payload_size); + CU_ASSERT(params.stateless_reset_token_present == + nparams->stateless_reset_token_present); + CU_ASSERT(params.ack_delay_exponent == nparams->ack_delay_exponent); + CU_ASSERT(params.disable_active_migration == + nparams->disable_active_migration); + CU_ASSERT(params.max_ack_delay == nparams->max_ack_delay); + CU_ASSERT(ngtcp2_cid_eq(¶ms.original_dcid, &nparams->original_dcid)); + CU_ASSERT(ngtcp2_cid_eq(¶ms.initial_scid, &nparams->initial_scid)); + CU_ASSERT(params.retry_scid_present == nparams->retry_scid_present); + + ngtcp2_transport_params_del(nparams, NULL); + memset(¶ms, 0, sizeof(params)); + + /* EE, all parameters */ + params.initial_max_stream_data_bidi_local = 1000000007; + params.initial_max_stream_data_bidi_remote = 961748941; + params.initial_max_stream_data_uni = 982451653; + params.initial_max_data = 1000000009; + params.initial_max_streams_bidi = 908; + params.initial_max_streams_uni = 16383; + params.max_idle_timeout = 16363 * NGTCP2_MILLISECONDS; + params.max_udp_payload_size = 1200; + params.stateless_reset_token_present = 1; + memset(params.stateless_reset_token, 0xf1, + sizeof(params.stateless_reset_token)); + params.ack_delay_exponent = 20; + params.preferred_address_present = 1; + sa_in = ¶ms.preferred_address.ipv4; + sa_in->sin_family = AF_INET; + memset(&sa_in->sin_addr, 0xf1, sizeof(sa_in->sin_addr)); + sa_in->sin_port = ngtcp2_htons(11732); + params.preferred_address.ipv4_present = 1; + params.preferred_address.ipv6_present = 0; + scid_init(¶ms.preferred_address.cid); + memset(params.preferred_address.stateless_reset_token, 0xd1, + sizeof(params.preferred_address.stateless_reset_token)); + params.disable_active_migration = 1; + params.max_ack_delay = 63 * NGTCP2_MILLISECONDS; + params.retry_scid_present = 1; + params.retry_scid = rcid; + params.original_dcid = dcid; + params.initial_scid = scid; + params.active_connection_id_limit = 1073741824; + params.max_datagram_frame_size = 63; + params.grease_quic_bit = 1; + params.version_info.chosen_version = NGTCP2_PROTO_VER_V1; + params.version_info.other_versions = other_versions; + params.version_info.other_versionslen = ngtcp2_arraylen(other_versions); + params.version_info_present = 1; + + len = + varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_LOCAL, + params.initial_max_stream_data_bidi_local) + + varint_paramlen( + NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_BIDI_REMOTE, + params.initial_max_stream_data_bidi_remote) + + varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAM_DATA_UNI, + params.initial_max_stream_data_uni) + + varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_DATA, + params.initial_max_data) + + varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_BIDI, + params.initial_max_streams_bidi) + + varint_paramlen(NGTCP2_TRANSPORT_PARAM_INITIAL_MAX_STREAMS_UNI, + params.initial_max_streams_uni) + + varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_IDLE_TIMEOUT, + params.max_idle_timeout / NGTCP2_MILLISECONDS) + + varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_UDP_PAYLOAD_SIZE, + params.max_udp_payload_size) + + varint_paramlen(NGTCP2_TRANSPORT_PARAM_ACK_DELAY_EXPONENT, + params.ack_delay_exponent) + + (ngtcp2_put_uvarintlen(NGTCP2_TRANSPORT_PARAM_DISABLE_ACTIVE_MIGRATION) + + ngtcp2_put_uvarintlen(0)) + + varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_ACK_DELAY, + params.max_ack_delay / NGTCP2_MILLISECONDS) + + varint_paramlen(NGTCP2_TRANSPORT_PARAM_ACTIVE_CONNECTION_ID_LIMIT, + params.active_connection_id_limit) + + (ngtcp2_put_uvarintlen(NGTCP2_TRANSPORT_PARAM_STATELESS_RESET_TOKEN) + + ngtcp2_put_uvarintlen(NGTCP2_STATELESS_RESET_TOKENLEN) + + NGTCP2_STATELESS_RESET_TOKENLEN) + + (ngtcp2_put_uvarintlen(NGTCP2_TRANSPORT_PARAM_PREFERRED_ADDRESS) + + ngtcp2_put_uvarintlen(4 + 2 + 16 + 2 + 1 + + params.preferred_address.cid.datalen + + NGTCP2_STATELESS_RESET_TOKENLEN) + + 4 + 2 + 16 + 2 + 1 + params.preferred_address.cid.datalen + + NGTCP2_STATELESS_RESET_TOKENLEN) + + (ngtcp2_put_uvarintlen( + NGTCP2_TRANSPORT_PARAM_RETRY_SOURCE_CONNECTION_ID) + + ngtcp2_put_uvarintlen(params.retry_scid.datalen) + + params.retry_scid.datalen) + + (ngtcp2_put_uvarintlen( + NGTCP2_TRANSPORT_PARAM_ORIGINAL_DESTINATION_CONNECTION_ID) + + ngtcp2_put_uvarintlen(params.original_dcid.datalen) + + params.original_dcid.datalen) + + (ngtcp2_put_uvarintlen( + NGTCP2_TRANSPORT_PARAM_INITIAL_SOURCE_CONNECTION_ID) + + ngtcp2_put_uvarintlen(params.initial_scid.datalen) + + params.initial_scid.datalen) + + varint_paramlen(NGTCP2_TRANSPORT_PARAM_MAX_DATAGRAM_FRAME_SIZE, + params.max_datagram_frame_size) + + (ngtcp2_put_uvarintlen(NGTCP2_TRANSPORT_PARAM_GREASE_QUIC_BIT) + + ngtcp2_put_uvarintlen(0)) + + (ngtcp2_put_uvarintlen(NGTCP2_TRANSPORT_PARAM_VERSION_INFORMATION_DRAFT) + + ngtcp2_put_uvarintlen(sizeof(params.version_info.chosen_version) + + params.version_info.other_versionslen) + + sizeof(params.version_info.chosen_version) + + params.version_info.other_versionslen); + + nwrite = ngtcp2_encode_transport_params( + buf, sizeof(buf), NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS, + ¶ms); + + CU_ASSERT((ngtcp2_ssize)len == nwrite); + + rv = ngtcp2_decode_transport_params_new( + &nparams, NGTCP2_TRANSPORT_PARAMS_TYPE_ENCRYPTED_EXTENSIONS, buf, + (size_t)nwrite, NULL); + + CU_ASSERT(0 == rv); + CU_ASSERT(params.initial_max_stream_data_bidi_local == + nparams->initial_max_stream_data_bidi_local); + CU_ASSERT(params.initial_max_stream_data_bidi_remote == + nparams->initial_max_stream_data_bidi_remote); + CU_ASSERT(params.initial_max_stream_data_uni == + nparams->initial_max_stream_data_uni); + CU_ASSERT(params.initial_max_data == nparams->initial_max_data); + CU_ASSERT(params.initial_max_streams_bidi == + nparams->initial_max_streams_bidi); + CU_ASSERT(params.initial_max_streams_uni == nparams->initial_max_streams_uni); + CU_ASSERT(params.max_idle_timeout == nparams->max_idle_timeout); + CU_ASSERT(params.max_udp_payload_size == nparams->max_udp_payload_size); + CU_ASSERT(0 == memcmp(params.stateless_reset_token, + nparams->stateless_reset_token, + sizeof(params.stateless_reset_token))); + CU_ASSERT(params.ack_delay_exponent == nparams->ack_delay_exponent); + CU_ASSERT(params.preferred_address_present == + nparams->preferred_address_present); + CU_ASSERT(0 == memcmp(¶ms.preferred_address.ipv4, + &nparams->preferred_address.ipv4, + sizeof(params.preferred_address.ipv4))); + CU_ASSERT(params.preferred_address.ipv4_present == + nparams->preferred_address.ipv4_present); + CU_ASSERT(0 == memcmp(¶ms.preferred_address.ipv6, + &nparams->preferred_address.ipv6, + sizeof(params.preferred_address.ipv6))); + CU_ASSERT(params.preferred_address.ipv6_present == + nparams->preferred_address.ipv6_present); + CU_ASSERT(ngtcp2_cid_eq(¶ms.preferred_address.cid, + &nparams->preferred_address.cid)); + CU_ASSERT(0 == + memcmp(params.preferred_address.stateless_reset_token, + nparams->preferred_address.stateless_reset_token, + sizeof(params.preferred_address.stateless_reset_token))); + CU_ASSERT(params.disable_active_migration == + nparams->disable_active_migration); + CU_ASSERT(params.max_ack_delay == nparams->max_ack_delay); + CU_ASSERT(params.retry_scid_present == nparams->retry_scid_present); + CU_ASSERT(ngtcp2_cid_eq(¶ms.retry_scid, &nparams->retry_scid)); + CU_ASSERT(ngtcp2_cid_eq(¶ms.initial_scid, &nparams->initial_scid)); + CU_ASSERT(ngtcp2_cid_eq(¶ms.original_dcid, &nparams->original_dcid)); + CU_ASSERT(params.active_connection_id_limit == + nparams->active_connection_id_limit); + CU_ASSERT(params.max_datagram_frame_size == nparams->max_datagram_frame_size); + CU_ASSERT(params.grease_quic_bit = nparams->grease_quic_bit); + CU_ASSERT(params.version_info_present == nparams->version_info_present); + CU_ASSERT(params.version_info.chosen_version == + nparams->version_info.chosen_version); + CU_ASSERT(0 == memcmp(params.version_info.other_versions, + nparams->version_info.other_versions, + params.version_info.other_versionslen)); + + ngtcp2_transport_params_del(nparams, NULL); +} diff --git a/tests/ngtcp2_crypto_test.h b/tests/ngtcp2_crypto_test.h new file mode 100644 index 0000000..e57c4bc --- /dev/null +++ b/tests/ngtcp2_crypto_test.h @@ -0,0 +1,35 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_CRYPTO_TEST_H +#define NGTCP2_CRYPTO_TEST_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +void test_ngtcp2_encode_transport_params(void); +void test_ngtcp2_decode_transport_params_new(void); + +#endif /* NGTCP2_CRYPTO_TEST_H */ diff --git a/tests/ngtcp2_gaptr_test.c b/tests/ngtcp2_gaptr_test.c new file mode 100644 index 0000000..5612b56 --- /dev/null +++ b/tests/ngtcp2_gaptr_test.c @@ -0,0 +1,127 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_gaptr_test.h" + +#include <CUnit/CUnit.h> + +#include "ngtcp2_gaptr.h" +#include "ngtcp2_test_helper.h" +#include "ngtcp2_mem.h" + +void test_ngtcp2_gaptr_push(void) { + ngtcp2_gaptr gaptr; + const ngtcp2_mem *mem = ngtcp2_mem_default(); + ngtcp2_ksl_it it; + ngtcp2_range r; + int rv; + size_t i; + + ngtcp2_gaptr_init(&gaptr, mem); + + rv = ngtcp2_gaptr_push(&gaptr, 0, 1); + + CU_ASSERT(0 == rv); + + it = ngtcp2_ksl_begin(&gaptr.gap); + r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it); + + CU_ASSERT(1 == r.begin); + CU_ASSERT(UINT64_MAX == r.end); + + rv = ngtcp2_gaptr_push(&gaptr, 12389, 133); + + CU_ASSERT(0 == rv); + + it = ngtcp2_ksl_begin(&gaptr.gap); + r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it); + + CU_ASSERT(1 == r.begin); + CU_ASSERT(12389 == r.end); + + ngtcp2_ksl_it_next(&it); + r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it); + + CU_ASSERT(12389 + 133 == r.begin); + CU_ASSERT(UINT64_MAX == r.end); + + for (i = 0; i < 2; ++i) { + rv = ngtcp2_gaptr_push(&gaptr, 1, 12389); + + CU_ASSERT(0 == rv); + + it = ngtcp2_ksl_begin(&gaptr.gap); + r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it); + + CU_ASSERT(12389 + 133 == r.begin); + CU_ASSERT(UINT64_MAX == r.end); + } + + rv = ngtcp2_gaptr_push(&gaptr, 12389 + 133 - 1, 2); + + CU_ASSERT(0 == rv); + + it = ngtcp2_ksl_begin(&gaptr.gap); + r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it); + + CU_ASSERT(12389 + 133 + 1 == r.begin); + CU_ASSERT(UINT64_MAX == r.end); + + ngtcp2_gaptr_free(&gaptr); +} + +void test_ngtcp2_gaptr_is_pushed(void) { + ngtcp2_gaptr gaptr; + const ngtcp2_mem *mem = ngtcp2_mem_default(); + int rv; + + ngtcp2_gaptr_init(&gaptr, mem); + + rv = ngtcp2_gaptr_push(&gaptr, 1000000007, 1009); + + CU_ASSERT(0 == rv); + CU_ASSERT(ngtcp2_gaptr_is_pushed(&gaptr, 1000000007, 1009)); + CU_ASSERT(!ngtcp2_gaptr_is_pushed(&gaptr, 1000000007, 1010)); + + ngtcp2_gaptr_free(&gaptr); +} + +void test_ngtcp2_gaptr_drop_first_gap(void) { + ngtcp2_gaptr gaptr; + const ngtcp2_mem *mem = ngtcp2_mem_default(); + int rv; + + ngtcp2_gaptr_init(&gaptr, mem); + + rv = ngtcp2_gaptr_push(&gaptr, 113245, 12); + + CU_ASSERT(0 == rv); + + ngtcp2_gaptr_drop_first_gap(&gaptr); + + CU_ASSERT(ngtcp2_gaptr_is_pushed(&gaptr, 0, 1)); + CU_ASSERT(113245 + 12 == ngtcp2_gaptr_first_gap_offset(&gaptr)); + + ngtcp2_gaptr_free(&gaptr); +} diff --git a/tests/ngtcp2_gaptr_test.h b/tests/ngtcp2_gaptr_test.h new file mode 100644 index 0000000..cc6507c --- /dev/null +++ b/tests/ngtcp2_gaptr_test.h @@ -0,0 +1,36 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_GAPTR_TEST_H +#define NGTCP2_GAPTR_TEST_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +void test_ngtcp2_gaptr_push(void); +void test_ngtcp2_gaptr_is_pushed(void); +void test_ngtcp2_gaptr_drop_first_gap(void); + +#endif /* NGTCP2_GAPTR_TEST_H */ diff --git a/tests/ngtcp2_idtr_test.c b/tests/ngtcp2_idtr_test.c new file mode 100644 index 0000000..90d15ba --- /dev/null +++ b/tests/ngtcp2_idtr_test.c @@ -0,0 +1,79 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_idtr_test.h" + +#include <CUnit/CUnit.h> + +#include "ngtcp2_idtr.h" +#include "ngtcp2_test_helper.h" +#include "ngtcp2_mem.h" + +static int64_t stream_id_from_id(uint64_t id) { return (int64_t)(id * 4); } + +void test_ngtcp2_idtr_open(void) { + const ngtcp2_mem *mem = ngtcp2_mem_default(); + ngtcp2_idtr idtr; + int rv; + ngtcp2_ksl_it it; + ngtcp2_range key; + + ngtcp2_idtr_init(&idtr, 0, mem); + + rv = ngtcp2_idtr_open(&idtr, stream_id_from_id(0)); + + CU_ASSERT(0 == rv); + + it = ngtcp2_ksl_begin(&idtr.gap.gap); + key = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it); + + CU_ASSERT(1 == key.begin); + CU_ASSERT(UINT64_MAX == key.end); + + rv = ngtcp2_idtr_open(&idtr, stream_id_from_id(1000000007)); + + CU_ASSERT(0 == rv); + + it = ngtcp2_ksl_begin(&idtr.gap.gap); + key = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it); + + CU_ASSERT(1 == key.begin); + CU_ASSERT(1000000007 == key.end); + + ngtcp2_ksl_it_next(&it); + key = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it); + + CU_ASSERT(1000000008 == key.begin); + CU_ASSERT(UINT64_MAX == key.end); + + rv = ngtcp2_idtr_open(&idtr, stream_id_from_id(0)); + + CU_ASSERT(NGTCP2_ERR_STREAM_IN_USE == rv); + + rv = ngtcp2_idtr_open(&idtr, stream_id_from_id(1000000007)); + + CU_ASSERT(NGTCP2_ERR_STREAM_IN_USE == rv); + + ngtcp2_idtr_free(&idtr); +} diff --git a/tests/ngtcp2_idtr_test.h b/tests/ngtcp2_idtr_test.h new file mode 100644 index 0000000..ebba934 --- /dev/null +++ b/tests/ngtcp2_idtr_test.h @@ -0,0 +1,34 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_IDTR_TEST_H +#define NGTCP2_IDTR_TEST_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +void test_ngtcp2_idtr_open(void); + +#endif /* NGTCP2_IDTR_TEST_H */ diff --git a/tests/ngtcp2_ksl_test.c b/tests/ngtcp2_ksl_test.c new file mode 100644 index 0000000..54bdd1b --- /dev/null +++ b/tests/ngtcp2_ksl_test.c @@ -0,0 +1,502 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_ksl_test.h" + +#include <CUnit/CUnit.h> + +#include "ngtcp2_ksl.h" +#include "ngtcp2_test_helper.h" + +static int less(const ngtcp2_ksl_key *lhs, const ngtcp2_ksl_key *rhs) { + return *(int64_t *)lhs < *(int64_t *)rhs; +} + +void test_ngtcp2_ksl_insert(void) { + static const int64_t keys[] = {10, 3, 8, 11, 16, 12, 1, 5, 4, + 0, 13, 7, 9, 2, 14, 6, 15}; + ngtcp2_ksl ksl; + const ngtcp2_mem *mem = ngtcp2_mem_default(); + size_t i; + ngtcp2_ksl_it it; + int64_t k; + + ngtcp2_ksl_init(&ksl, less, sizeof(int64_t), mem); + + for (i = 0; i < ngtcp2_arraylen(keys); ++i) { + CU_ASSERT(0 == ngtcp2_ksl_insert(&ksl, NULL, &keys[i], NULL)); + it = ngtcp2_ksl_lower_bound(&ksl, &keys[i]); + + CU_ASSERT(keys[i] == *(int64_t *)ngtcp2_ksl_it_key(&it)); + } + + for (i = 0; i < ngtcp2_arraylen(keys); ++i) { + CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &keys[i])); + it = ngtcp2_ksl_lower_bound(&ksl, &keys[i]); + if (!ngtcp2_ksl_it_end(&it)) { + CU_ASSERT(keys[i] < *(int64_t *)ngtcp2_ksl_it_key(&it)); + } + } + + ngtcp2_ksl_free(&ksl); + + /* check the case that the right end range is removed */ + ngtcp2_ksl_init(&ksl, less, sizeof(int64_t), mem); + + for (i = 0; i < 32; ++i) { + k = (int64_t)i; + CU_ASSERT(0 == ngtcp2_ksl_insert(&ksl, NULL, &k, NULL)); + } + + /* Removing 15 which is the last node in a blk. */ + k = 15; + CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, &it, &k)); + + CU_ASSERT(16 == *(int64_t *)ngtcp2_ksl_it_key(&it)); + + /* Insert 15 again works */ + CU_ASSERT(0 == ngtcp2_ksl_insert(&ksl, NULL, &k, NULL)); + + k = 15; + it = ngtcp2_ksl_lower_bound(&ksl, &k); + + CU_ASSERT(15 == *(int64_t *)ngtcp2_ksl_it_key(&it)); + + ngtcp2_ksl_free(&ksl); + + /* Check the case that the intermediate node contains smaller key + than ancestor node. Make sure that inserting key larger than + that still works.*/ + ngtcp2_ksl_init(&ksl, less, sizeof(int64_t), mem); + + for (i = 0; i < 760; ++i) { + k = (int64_t)i; + CU_ASSERT(0 == ngtcp2_ksl_insert(&ksl, NULL, &k, NULL)); + } + + k = 255; + CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &k)); + k = 254; + CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &k)); + k = 253; + CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &k)); + + k = 253; + CU_ASSERT(0 == ngtcp2_ksl_insert(&ksl, NULL, &k, NULL)); + it = ngtcp2_ksl_lower_bound(&ksl, &k); + + CU_ASSERT(253 == *(int64_t *)ngtcp2_ksl_it_key(&it)); + + ngtcp2_ksl_free(&ksl); + + /* check merge node (head) */ + ngtcp2_ksl_init(&ksl, less, sizeof(int64_t), mem); + + for (i = 0; i < 32; ++i) { + k = (int64_t)i; + CU_ASSERT(0 == ngtcp2_ksl_insert(&ksl, NULL, &k, NULL)); + } + + /* Removing these 3 nodes kicks merging 2 nodes under head */ + k = 15; + CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &k)); + k = 14; + CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &k)); + k = 13; + CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &k)); + + CU_ASSERT(29 == ksl.head->n); + + ngtcp2_ksl_free(&ksl); + + /* check merge node (non head) */ + ngtcp2_ksl_init(&ksl, less, sizeof(int64_t), mem); + + for (i = 0; i < 32 + 18; ++i) { + k = (int64_t)i; + CU_ASSERT(0 == ngtcp2_ksl_insert(&ksl, NULL, &k, NULL)); + } + + /* Removing these 3 nodes kicks merging 2 nodes */ + k = 15; + CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &k)); + k = 14; + CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &k)); + k = 13; + CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &k)); + + CU_ASSERT(2 == ksl.head->n); + CU_ASSERT(29 == ngtcp2_ksl_nth_node(&ksl, ksl.head, 0)->blk->n); + CU_ASSERT(18 == ngtcp2_ksl_nth_node(&ksl, ksl.head, 1)->blk->n); + + ngtcp2_ksl_free(&ksl); + + /* Iterate backwards */ + ngtcp2_ksl_init(&ksl, less, sizeof(int64_t), mem); + + /* split nodes */ + for (i = 0; i < 100; ++i) { + k = (int64_t)i; + CU_ASSERT(0 == ngtcp2_ksl_insert(&ksl, NULL, &k, NULL)); + } + + /* merge nodes */ + for (i = 0; i < 50; ++i) { + k = (int64_t)i; + CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &k)); + } + + i = 99; + for (it = ngtcp2_ksl_end(&ksl); !ngtcp2_ksl_it_begin(&it);) { + ngtcp2_ksl_it_prev(&it); + + CU_ASSERT((int64_t)i-- == *(int64_t *)ngtcp2_ksl_it_key(&it)); + } + + /* head only */ + for (i = 50; i < 88; ++i) { + k = (int64_t)i; + CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &k)); + } + + i = 99; + for (it = ngtcp2_ksl_end(&ksl); !ngtcp2_ksl_it_begin(&it);) { + ngtcp2_ksl_it_prev(&it); + + CU_ASSERT((int64_t)i-- == *(int64_t *)ngtcp2_ksl_it_key(&it)); + } + + ngtcp2_ksl_free(&ksl); +} + +void test_ngtcp2_ksl_clear(void) { + ngtcp2_ksl ksl; + const ngtcp2_mem *mem = ngtcp2_mem_default(); + ngtcp2_ksl_it it; + size_t i; + int64_t k; + + ngtcp2_ksl_init(&ksl, less, sizeof(int64_t), mem); + + for (i = 0; i < 100; ++i) { + k = (int64_t)i; + CU_ASSERT(0 == ngtcp2_ksl_insert(&ksl, NULL, &k, NULL)); + } + + ngtcp2_ksl_clear(&ksl); + + CU_ASSERT(0 == ngtcp2_ksl_len(&ksl)); + + it = ngtcp2_ksl_begin(&ksl); + + CU_ASSERT(ngtcp2_ksl_it_end(&it)); + + it = ngtcp2_ksl_end(&ksl); + + CU_ASSERT(ngtcp2_ksl_it_end(&it)); + + ngtcp2_ksl_free(&ksl); +} + +void test_ngtcp2_ksl_range(void) { + static const ngtcp2_range keys[] = { + {10, 11}, {3, 4}, {8, 9}, {11, 12}, {16, 17}, {12, 13}, + {1, 2}, {5, 6}, {4, 5}, {0, 1}, {13, 14}, {7, 8}, + {9, 10}, {2, 3}, {14, 15}, {6, 7}, {15, 16}, {17, 18}, + {18, 19}, {19, 20}, {20, 21}, {202, 203}, {203, 204}, {204, 205}, + {205, 206}, {206, 207}, {207, 208}, {208, 209}, {209, 210}, {210, 211}, + {211, 212}, {212, 213}, {213, 214}, {214, 215}, {215, 216}}; + ngtcp2_ksl ksl; + const ngtcp2_mem *mem = ngtcp2_mem_default(); + size_t i; + ngtcp2_range r; + ngtcp2_ksl_it it; + ngtcp2_ksl_node *node; + + ngtcp2_ksl_init(&ksl, ngtcp2_ksl_range_compar, sizeof(ngtcp2_range), mem); + + for (i = 0; i < ngtcp2_arraylen(keys); ++i) { + CU_ASSERT(0 == ngtcp2_ksl_insert(&ksl, NULL, &keys[i], NULL)); + it = ngtcp2_ksl_lower_bound_compar(&ksl, &keys[i], + ngtcp2_ksl_range_exclusive_compar); + r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it); + + CU_ASSERT(ngtcp2_range_eq(&keys[i], &r)); + } + + for (i = 0; i < ngtcp2_arraylen(keys); ++i) { + CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &keys[i])); + it = ngtcp2_ksl_lower_bound_compar(&ksl, &keys[i], + ngtcp2_ksl_range_exclusive_compar); + + if (!ngtcp2_ksl_it_end(&it)) { + r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it); + + CU_ASSERT(keys[i].end <= r.begin); + } + } + + ngtcp2_ksl_free(&ksl); + + /* check merge node (head) */ + ngtcp2_ksl_init(&ksl, ngtcp2_ksl_range_compar, sizeof(ngtcp2_range), mem); + + for (i = 0; i < 32; ++i) { + ngtcp2_range_init(&r, i, i + 1); + CU_ASSERT(0 == ngtcp2_ksl_insert(&ksl, NULL, &r, NULL)); + } + + /* Removing these 3 nodes kicks merging 2 nodes under head */ + ngtcp2_range_init(&r, 13, 14); + CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &r)); + + ngtcp2_range_init(&r, 14, 15); + CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &r)); + + ngtcp2_range_init(&r, 15, 16); + CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &r)); + + CU_ASSERT(29 == ksl.head->n); + + ngtcp2_ksl_free(&ksl); + + /* check merge node (non head) */ + ngtcp2_ksl_init(&ksl, ngtcp2_ksl_range_compar, sizeof(ngtcp2_range), mem); + + for (i = 0; i < 32 + 18; ++i) { + ngtcp2_range_init(&r, i, i + 1); + CU_ASSERT(0 == ngtcp2_ksl_insert(&ksl, NULL, &r, NULL)); + } + + /* Removing these 3 nodes kicks merging 2 nodes */ + ngtcp2_range_init(&r, 13, 14); + CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &r)); + + ngtcp2_range_init(&r, 14, 15); + CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &r)); + + ngtcp2_range_init(&r, 15, 16); + CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &r)); + + CU_ASSERT(2 == ksl.head->n); + CU_ASSERT(29 == ngtcp2_ksl_nth_node(&ksl, ksl.head, 0)->blk->n); + CU_ASSERT(18 == ngtcp2_ksl_nth_node(&ksl, ksl.head, 1)->blk->n); + + ngtcp2_ksl_free(&ksl); + + /* shift_left */ + ngtcp2_ksl_init(&ksl, ngtcp2_ksl_range_compar, sizeof(ngtcp2_range), mem); + + for (i = 1; i < 6400; i += 100) { + ngtcp2_range_init(&r, i, i + 1); + CU_ASSERT(0 == ngtcp2_ksl_insert(&ksl, NULL, &r, NULL)); + } + + ngtcp2_range_init(&r, 1501, 1502); + CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &r)); + ngtcp2_range_init(&r, 1401, 1402); + CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &r)); + + r = *(ngtcp2_range *)(void *)ngtcp2_ksl_nth_node( + &ksl, ngtcp2_ksl_nth_node(&ksl, ksl.head, 1)->blk, 0) + ->key; + + CU_ASSERT(1701 == r.begin); + + ngtcp2_ksl_free(&ksl); + + /* shift_right */ + ngtcp2_ksl_init(&ksl, ngtcp2_ksl_range_compar, sizeof(ngtcp2_range), mem); + + for (i = 0; i < 32; ++i) { + ngtcp2_range_init(&r, i, i + 1); + CU_ASSERT(0 == ngtcp2_ksl_insert(&ksl, NULL, &r, NULL)); + } + + ngtcp2_range_init(&r, 17, 18); + CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &r)); + ngtcp2_range_init(&r, 16, 17); + CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &r)); + + node = ngtcp2_ksl_nth_node(&ksl, ksl.head, 0); + r = *(ngtcp2_range *)(void *)ngtcp2_ksl_nth_node(&ksl, node->blk, + node->blk->n - 1) + ->key; + + CU_ASSERT(14 == r.begin); + + ngtcp2_ksl_free(&ksl); +} + +void test_ngtcp2_ksl_update_key_range(void) { + static ngtcp2_range ranges[] = { + {0, 5}, {10, 15}, {20, 25}, {30, 35}, {40, 45}, {50, 55}, + {60, 65}, {70, 75}, {80, 85}, {90, 95}, {100, 105}, {110, 115}, + {120, 125}, {130, 135}, {140, 145}, {150, 155}, {160, 165}, {170, 175}}; + ngtcp2_ksl ksl; + const ngtcp2_mem *mem = ngtcp2_mem_default(); + size_t i; + ngtcp2_range r; + ngtcp2_ksl_it it; + + ngtcp2_ksl_init(&ksl, ngtcp2_ksl_range_compar, sizeof(ngtcp2_range), mem); + + for (i = 0; i < ngtcp2_arraylen(ranges); ++i) { + CU_ASSERT(0 == ngtcp2_ksl_insert(&ksl, NULL, &ranges[i], NULL)); + } + + r.begin = 70; + r.end = 72; + ngtcp2_ksl_update_key(&ksl, &ranges[7], &r); + + r.begin = 73; + r.end = 74; + CU_ASSERT(0 == ngtcp2_ksl_insert(&ksl, NULL, &r, NULL)); + + r.begin = 74; + r.end = 75; + CU_ASSERT(0 == ngtcp2_ksl_insert(&ksl, NULL, &r, NULL)); + + r.begin = 74; + r.end = 75; + it = ngtcp2_ksl_lower_bound_compar(&ksl, &r, + ngtcp2_ksl_range_exclusive_compar); + + r = *(ngtcp2_range *)ngtcp2_ksl_it_key(&it); + + CU_ASSERT(r.begin == 74); + CU_ASSERT(r.end == 75); + + ngtcp2_ksl_free(&ksl); +} + +static void shuffle(int64_t *a, size_t n) { + size_t i, j; + int64_t t; + + for (i = n - 1; i >= 1; --i) { + j = (size_t)((double)(i + 1) * rand() / (RAND_MAX + 1.0)); + t = a[j]; + a[j] = a[i]; + a[i] = t; + } +} + +void test_ngtcp2_ksl_dup(void) { + static int64_t keys[16000]; + size_t i, j; + ngtcp2_ksl ksl; + const ngtcp2_mem *mem = ngtcp2_mem_default(); + ngtcp2_ksl_it it; + + for (i = 0; i < ngtcp2_arraylen(keys); ++i) { + keys[i] = (int64_t)i; + } + + for (j = 0; j < 10; ++j) { + ngtcp2_ksl_init(&ksl, less, sizeof(int64_t), mem); + + shuffle(keys, ngtcp2_arraylen(keys)); + + for (i = 0; i < ngtcp2_arraylen(keys); ++i) { + CU_ASSERT(0 == ngtcp2_ksl_insert(&ksl, NULL, &keys[i], NULL)); + CU_ASSERT(NGTCP2_ERR_INVALID_ARGUMENT == + ngtcp2_ksl_insert(&ksl, NULL, &keys[i], NULL)); + + it = ngtcp2_ksl_lower_bound(&ksl, &keys[i]); + + CU_ASSERT(keys[i] == *(int64_t *)ngtcp2_ksl_it_key(&it)); + } + + shuffle(keys, ngtcp2_arraylen(keys)); + + for (i = 0; i < ngtcp2_arraylen(keys); ++i) { + CU_ASSERT(0 == ngtcp2_ksl_remove(&ksl, NULL, &keys[i])); + + it = ngtcp2_ksl_begin(&ksl); + + CU_ASSERT(NGTCP2_ERR_INVALID_ARGUMENT == + ngtcp2_ksl_remove(&ksl, &it, &keys[i])); + CU_ASSERT(ngtcp2_ksl_it_end(&it)); + + it = ngtcp2_ksl_lower_bound(&ksl, &keys[i]); + + CU_ASSERT(ngtcp2_ksl_it_end(&it) || + keys[i] < *(int64_t *)ngtcp2_ksl_it_key(&it)); + + CU_ASSERT(0 == ngtcp2_ksl_insert(&ksl, NULL, &keys[i], NULL)); + + it = ngtcp2_ksl_begin(&ksl); + + CU_ASSERT(NGTCP2_ERR_INVALID_ARGUMENT == + ngtcp2_ksl_insert(&ksl, &it, &keys[i], NULL)); + CU_ASSERT(ngtcp2_ksl_it_end(&it)); + + it = ngtcp2_ksl_lower_bound(&ksl, &keys[i]); + CU_ASSERT(keys[i] == *(int64_t *)ngtcp2_ksl_it_key(&it)); + } + + ngtcp2_ksl_free(&ksl); + } +} + +void test_ngtcp2_ksl_remove_hint(void) { + static int64_t keys[16000]; + ngtcp2_ksl ksl; + const ngtcp2_mem *mem = ngtcp2_mem_default(); + ngtcp2_ksl_it it; + size_t i, j; + + for (i = 0; i < ngtcp2_arraylen(keys); ++i) { + keys[i] = (int64_t)i; + } + + for (j = 0; j < 10; ++j) { + ngtcp2_ksl_init(&ksl, less, sizeof(int64_t), mem); + + shuffle(keys, ngtcp2_arraylen(keys)); + + for (i = 0; i < ngtcp2_arraylen(keys); ++i) { + CU_ASSERT(0 == ngtcp2_ksl_insert(&ksl, NULL, &keys[i], NULL)); + } + + shuffle(keys, ngtcp2_arraylen(keys)); + + for (i = 0; i < ngtcp2_arraylen(keys); ++i) { + it = ngtcp2_ksl_lower_bound(&ksl, &keys[i]); + + CU_ASSERT(!ngtcp2_ksl_it_end(&it)); + CU_ASSERT(keys[i] == *(int64_t *)ngtcp2_ksl_it_key(&it)); + CU_ASSERT(0 == ngtcp2_ksl_remove_hint(&ksl, &it, &it, &keys[i])); + + it = ngtcp2_ksl_lower_bound(&ksl, &keys[i]); + + CU_ASSERT(ngtcp2_ksl_it_end(&it) || + keys[i] != *(int64_t *)ngtcp2_ksl_it_key(&it)); + CU_ASSERT(ngtcp2_arraylen(keys) - i - 1 == ngtcp2_ksl_len(&ksl)); + } + + ngtcp2_ksl_free(&ksl); + } +} diff --git a/tests/ngtcp2_ksl_test.h b/tests/ngtcp2_ksl_test.h new file mode 100644 index 0000000..9cdc4d4 --- /dev/null +++ b/tests/ngtcp2_ksl_test.h @@ -0,0 +1,39 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_KSL_TEST_H +#define NGTCP2_KSL_TEST_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +void test_ngtcp2_ksl_insert(void); +void test_ngtcp2_ksl_clear(void); +void test_ngtcp2_ksl_range(void); +void test_ngtcp2_ksl_update_key_range(void); +void test_ngtcp2_ksl_dup(void); +void test_ngtcp2_ksl_remove_hint(void); + +#endif /* NGTCP2_KSL_TEST_H */ diff --git a/tests/ngtcp2_map_test.c b/tests/ngtcp2_map_test.c new file mode 100644 index 0000000..8ee4086 --- /dev/null +++ b/tests/ngtcp2_map_test.c @@ -0,0 +1,206 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2012 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_map_test.h" + +#include <CUnit/CUnit.h> + +#include "ngtcp2_map.h" + +typedef struct strentry { + ngtcp2_map_key_type key; + const char *str; +} strentry; + +static void strentry_init(strentry *entry, ngtcp2_map_key_type key, + const char *str) { + entry->key = key; + entry->str = str; +} + +void test_ngtcp2_map(void) { + strentry foo, FOO, bar, baz, shrubbery; + ngtcp2_map map; + ngtcp2_map_init(&map, ngtcp2_mem_default()); + + strentry_init(&foo, 1, "foo"); + strentry_init(&FOO, 1, "FOO"); + strentry_init(&bar, 2, "bar"); + strentry_init(&baz, 3, "baz"); + strentry_init(&shrubbery, 4, "shrubbery"); + + CU_ASSERT(0 == ngtcp2_map_insert(&map, foo.key, &foo)); + CU_ASSERT(strcmp("foo", ((strentry *)ngtcp2_map_find(&map, 1))->str) == 0); + CU_ASSERT(1 == ngtcp2_map_size(&map)); + + CU_ASSERT(NGTCP2_ERR_INVALID_ARGUMENT == + ngtcp2_map_insert(&map, FOO.key, &FOO)); + + CU_ASSERT(1 == ngtcp2_map_size(&map)); + CU_ASSERT(strcmp("foo", ((strentry *)ngtcp2_map_find(&map, 1))->str) == 0); + + CU_ASSERT(0 == ngtcp2_map_insert(&map, bar.key, &bar)); + CU_ASSERT(2 == ngtcp2_map_size(&map)); + + CU_ASSERT(0 == ngtcp2_map_insert(&map, baz.key, &baz)); + CU_ASSERT(3 == ngtcp2_map_size(&map)); + + CU_ASSERT(0 == ngtcp2_map_insert(&map, shrubbery.key, &shrubbery)); + CU_ASSERT(4 == ngtcp2_map_size(&map)); + + CU_ASSERT(strcmp("baz", ((strentry *)ngtcp2_map_find(&map, 3))->str) == 0); + + ngtcp2_map_remove(&map, 3); + CU_ASSERT(3 == ngtcp2_map_size(&map)); + CU_ASSERT(NULL == ngtcp2_map_find(&map, 3)); + + ngtcp2_map_remove(&map, 1); + CU_ASSERT(2 == ngtcp2_map_size(&map)); + CU_ASSERT(NULL == ngtcp2_map_find(&map, 1)); + + /* Erasing non-existent entry */ + ngtcp2_map_remove(&map, 1); + CU_ASSERT(2 == ngtcp2_map_size(&map)); + CU_ASSERT(NULL == ngtcp2_map_find(&map, 1)); + + CU_ASSERT(strcmp("bar", ((strentry *)ngtcp2_map_find(&map, 2))->str) == 0); + CU_ASSERT(strcmp("shrubbery", ((strentry *)ngtcp2_map_find(&map, 4))->str) == + 0); + + ngtcp2_map_free(&map); +} + +static void shuffle(int *a, int n) { + int i; + for (i = n - 1; i >= 1; --i) { + size_t j = (size_t)((double)(i + 1) * rand() / (RAND_MAX + 1.0)); + int t = a[j]; + a[j] = a[i]; + a[i] = t; + } +} + +static int eachfun(void *data, void *ptr) { + (void)data; + (void)ptr; + + return 0; +} + +#define NUM_ENT 6000 +static strentry arr[NUM_ENT]; +static int order[NUM_ENT]; + +void test_ngtcp2_map_functional(void) { + ngtcp2_map map; + int i; + strentry *ent; + + ngtcp2_map_init(&map, ngtcp2_mem_default()); + for (i = 0; i < NUM_ENT; ++i) { + strentry_init(&arr[i], (ngtcp2_map_key_type)(i + 1), "foo"); + order[i] = i + 1; + } + /* insertion */ + shuffle(order, NUM_ENT); + for (i = 0; i < NUM_ENT; ++i) { + ent = &arr[order[i] - 1]; + CU_ASSERT(0 == ngtcp2_map_insert(&map, ent->key, ent)); + } + + CU_ASSERT(NUM_ENT == ngtcp2_map_size(&map)); + + /* traverse */ + ngtcp2_map_each(&map, eachfun, NULL); + /* find */ + shuffle(order, NUM_ENT); + for (i = 0; i < NUM_ENT; ++i) { + CU_ASSERT(NULL != ngtcp2_map_find(&map, (ngtcp2_map_key_type)order[i])); + } + /* remove */ + for (i = 0; i < NUM_ENT; ++i) { + CU_ASSERT(0 == ngtcp2_map_remove(&map, (ngtcp2_map_key_type)order[i])); + } + + /* each_free (but no op function for testing purpose) */ + for (i = 0; i < NUM_ENT; ++i) { + strentry_init(&arr[i], (ngtcp2_map_key_type)(i + 1), "foo"); + } + /* insert once again */ + for (i = 0; i < NUM_ENT; ++i) { + ent = &arr[i]; + CU_ASSERT(0 == ngtcp2_map_insert(&map, ent->key, ent)); + } + ngtcp2_map_each_free(&map, eachfun, NULL); + ngtcp2_map_free(&map); +} + +static int entry_free(void *data, void *ptr) { + const ngtcp2_mem *mem = ptr; + + mem->free(data, NULL); + return 0; +} + +void test_ngtcp2_map_each_free(void) { + const ngtcp2_mem *mem = ngtcp2_mem_default(); + strentry *foo = mem->malloc(sizeof(strentry), NULL), + *bar = mem->malloc(sizeof(strentry), NULL), + *baz = mem->malloc(sizeof(strentry), NULL), + *shrubbery = mem->malloc(sizeof(strentry), NULL); + ngtcp2_map map; + ngtcp2_map_init(&map, ngtcp2_mem_default()); + + strentry_init(foo, 1, "foo"); + strentry_init(bar, 2, "bar"); + strentry_init(baz, 3, "baz"); + strentry_init(shrubbery, 4, "shrubbery"); + + ngtcp2_map_insert(&map, foo->key, foo); + ngtcp2_map_insert(&map, bar->key, bar); + ngtcp2_map_insert(&map, baz->key, baz); + ngtcp2_map_insert(&map, shrubbery->key, shrubbery); + + ngtcp2_map_each_free(&map, entry_free, (void *)mem); + ngtcp2_map_free(&map); +} + +void test_ngtcp2_map_clear(void) { + const ngtcp2_mem *mem = ngtcp2_mem_default(); + ngtcp2_map map; + strentry foo; + + strentry_init(&foo, 1, "foo"); + + ngtcp2_map_init(&map, mem); + + CU_ASSERT(0 == ngtcp2_map_insert(&map, foo.key, &foo)); + + ngtcp2_map_clear(&map); + + CU_ASSERT(0 == ngtcp2_map_size(&map)); + + ngtcp2_map_free(&map); +} diff --git a/tests/ngtcp2_map_test.h b/tests/ngtcp2_map_test.h new file mode 100644 index 0000000..c8dd3eb --- /dev/null +++ b/tests/ngtcp2_map_test.h @@ -0,0 +1,38 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * Copyright (c) 2012 nghttp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_MAP_TEST_H +#define NGTCP2_MAP_TEST_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +void test_ngtcp2_map(void); +void test_ngtcp2_map_functional(void); +void test_ngtcp2_map_each_free(void); +void test_ngtcp2_map_clear(void); + +#endif /* NGTCP2_MAP_TEST_H */ diff --git a/tests/ngtcp2_pkt_test.c b/tests/ngtcp2_pkt_test.c new file mode 100644 index 0000000..f225610 --- /dev/null +++ b/tests/ngtcp2_pkt_test.c @@ -0,0 +1,1644 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_pkt_test.h" + +#include <CUnit/CUnit.h> + +#include "ngtcp2_pkt.h" +#include "ngtcp2_test_helper.h" +#include "ngtcp2_conv.h" +#include "ngtcp2_cid.h" +#include "ngtcp2_str.h" +#include "ngtcp2_vec.h" + +static int null_retry_encrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *plaintext, size_t plaintextlen, + const uint8_t *nonce, size_t noncelen, + const uint8_t *aad, size_t aadlen) { + (void)dest; + (void)aead; + (void)aead_ctx; + (void)plaintext; + (void)plaintextlen; + (void)nonce; + (void)noncelen; + (void)aad; + (void)aadlen; + + if (plaintextlen && plaintext != dest) { + memcpy(dest, plaintext, plaintextlen); + } + memset(dest + plaintextlen, 0, NGTCP2_RETRY_TAGLEN); + + return 0; +} + +void test_ngtcp2_pkt_decode_version_cid(void) { + uint8_t buf[NGTCP2_MAX_UDP_PAYLOAD_SIZE]; + ngtcp2_version_cid vc; + int rv; + uint8_t *p; + + /* Supported QUIC version */ + p = buf; + *p++ = NGTCP2_HEADER_FORM_BIT; + p = ngtcp2_put_uint32be(p, NGTCP2_PROTO_VER_V1); + *p++ = NGTCP2_MAX_CIDLEN; + p = ngtcp2_setmem(p, 0xf1, NGTCP2_MAX_CIDLEN); + *p++ = NGTCP2_MAX_CIDLEN - 1; + p = ngtcp2_setmem(p, 0xf2, NGTCP2_MAX_CIDLEN - 1); + + rv = ngtcp2_pkt_decode_version_cid(&vc, buf, (size_t)(p - buf), 0); + + CU_ASSERT(0 == rv); + CU_ASSERT(NGTCP2_PROTO_VER_V1 == vc.version); + CU_ASSERT(NGTCP2_MAX_CIDLEN == vc.dcidlen); + CU_ASSERT(&buf[6] == vc.dcid); + CU_ASSERT(NGTCP2_MAX_CIDLEN - 1 == vc.scidlen); + CU_ASSERT(&buf[6 + NGTCP2_MAX_CIDLEN + 1] == vc.scid); + + /* Unsupported QUIC version */ + memset(buf, 0, sizeof(buf)); + p = buf; + *p++ = NGTCP2_HEADER_FORM_BIT; + p = ngtcp2_put_uint32be(p, 0xffffff00); + *p++ = NGTCP2_MAX_CIDLEN; + p = ngtcp2_setmem(p, 0xf1, NGTCP2_MAX_CIDLEN); + *p++ = NGTCP2_MAX_CIDLEN - 1; + ngtcp2_setmem(p, 0xf2, NGTCP2_MAX_CIDLEN - 1); + + rv = ngtcp2_pkt_decode_version_cid(&vc, buf, sizeof(buf), 0); + + CU_ASSERT(NGTCP2_ERR_VERSION_NEGOTIATION == rv); + CU_ASSERT(0xffffff00 == vc.version); + CU_ASSERT(NGTCP2_MAX_CIDLEN == vc.dcidlen); + CU_ASSERT(&buf[6] == vc.dcid); + CU_ASSERT(NGTCP2_MAX_CIDLEN - 1 == vc.scidlen); + CU_ASSERT(&buf[6 + NGTCP2_MAX_CIDLEN + 1] == vc.scid); + + /* Unsupported QUIC version with UDP payload size < 1200 */ + p = buf; + *p++ = NGTCP2_HEADER_FORM_BIT; + p = ngtcp2_put_uint32be(p, 0xffffff00); + *p++ = NGTCP2_MAX_CIDLEN; + p = ngtcp2_setmem(p, 0xf1, NGTCP2_MAX_CIDLEN); + *p++ = NGTCP2_MAX_CIDLEN - 1; + p = ngtcp2_setmem(p, 0xf2, NGTCP2_MAX_CIDLEN - 1); + + rv = ngtcp2_pkt_decode_version_cid(&vc, buf, (size_t)(p - buf), 0); + + CU_ASSERT(NGTCP2_ERR_INVALID_ARGUMENT == rv); + + /* Supported QUIC version with long CID */ + p = buf; + *p++ = NGTCP2_HEADER_FORM_BIT; + p = ngtcp2_put_uint32be(p, NGTCP2_PROTO_VER_V1); + *p++ = NGTCP2_MAX_CIDLEN + 1; + p = ngtcp2_setmem(p, 0xf1, NGTCP2_MAX_CIDLEN + 1); + *p++ = NGTCP2_MAX_CIDLEN; + p = ngtcp2_setmem(p, 0xf2, NGTCP2_MAX_CIDLEN); + + rv = ngtcp2_pkt_decode_version_cid(&vc, buf, (size_t)(p - buf), 0); + + CU_ASSERT(NGTCP2_ERR_INVALID_ARGUMENT == rv); + + /* Unsupported QUIC version with long CID */ + memset(buf, 0, sizeof(buf)); + p = buf; + *p++ = NGTCP2_HEADER_FORM_BIT; + p = ngtcp2_put_uint32be(p, 0xffffff00); + *p++ = NGTCP2_MAX_CIDLEN + 1; + p = ngtcp2_setmem(p, 0xf1, NGTCP2_MAX_CIDLEN + 1); + *p++ = NGTCP2_MAX_CIDLEN; + ngtcp2_setmem(p, 0xf2, NGTCP2_MAX_CIDLEN); + + rv = ngtcp2_pkt_decode_version_cid(&vc, buf, sizeof(buf), 0); + + CU_ASSERT(NGTCP2_ERR_VERSION_NEGOTIATION == rv); + CU_ASSERT(0xffffff00 == vc.version); + CU_ASSERT(NGTCP2_MAX_CIDLEN + 1 == vc.dcidlen); + CU_ASSERT(&buf[6] == vc.dcid); + CU_ASSERT(NGTCP2_MAX_CIDLEN == vc.scidlen); + CU_ASSERT(&buf[6 + NGTCP2_MAX_CIDLEN + 1 + 1] == vc.scid); + + /* VN */ + p = buf; + *p++ = NGTCP2_HEADER_FORM_BIT; + p = ngtcp2_put_uint32be(p, 0); + *p++ = NGTCP2_MAX_CIDLEN; + p = ngtcp2_setmem(p, 0xf1, NGTCP2_MAX_CIDLEN); + *p++ = NGTCP2_MAX_CIDLEN - 1; + p = ngtcp2_setmem(p, 0xf2, NGTCP2_MAX_CIDLEN - 1); + + rv = ngtcp2_pkt_decode_version_cid(&vc, buf, (size_t)(p - buf), 0); + + CU_ASSERT(0 == rv); + CU_ASSERT(0 == vc.version); + CU_ASSERT(NGTCP2_MAX_CIDLEN == vc.dcidlen); + CU_ASSERT(&buf[6] == vc.dcid); + CU_ASSERT(NGTCP2_MAX_CIDLEN - 1 == vc.scidlen); + CU_ASSERT(&buf[6 + NGTCP2_MAX_CIDLEN + 1] == vc.scid); + + /* VN with long CID */ + p = buf; + *p++ = NGTCP2_HEADER_FORM_BIT; + p = ngtcp2_put_uint32be(p, 0); + *p++ = NGTCP2_MAX_CIDLEN + 1; + p = ngtcp2_setmem(p, 0xf1, NGTCP2_MAX_CIDLEN + 1); + *p++ = NGTCP2_MAX_CIDLEN; + p = ngtcp2_setmem(p, 0xf2, NGTCP2_MAX_CIDLEN); + + rv = ngtcp2_pkt_decode_version_cid(&vc, buf, (size_t)(p - buf), 0); + + CU_ASSERT(0 == rv); + CU_ASSERT(0 == vc.version); + CU_ASSERT(NGTCP2_MAX_CIDLEN + 1 == vc.dcidlen); + CU_ASSERT(&buf[6] == vc.dcid); + CU_ASSERT(NGTCP2_MAX_CIDLEN == vc.scidlen); + CU_ASSERT(&buf[6 + NGTCP2_MAX_CIDLEN + 1 + 1] == vc.scid); + + /* Malformed Long packet */ + p = buf; + *p++ = NGTCP2_HEADER_FORM_BIT; + p = ngtcp2_put_uint32be(p, NGTCP2_PROTO_VER_V1); + *p++ = NGTCP2_MAX_CIDLEN; + p = ngtcp2_setmem(p, 0xf1, NGTCP2_MAX_CIDLEN); + *p++ = NGTCP2_MAX_CIDLEN - 1; + p = ngtcp2_setmem(p, 0xf2, NGTCP2_MAX_CIDLEN - 1); + --p; + + rv = ngtcp2_pkt_decode_version_cid(&vc, buf, (size_t)(p - buf), 0); + + CU_ASSERT(NGTCP2_ERR_INVALID_ARGUMENT == rv); + + /* Short packet */ + p = buf; + *p++ = 0; + p = ngtcp2_setmem(p, 0xf1, NGTCP2_MAX_CIDLEN); + + rv = ngtcp2_pkt_decode_version_cid(&vc, buf, (size_t)(p - buf), + NGTCP2_MAX_CIDLEN); + + CU_ASSERT(0 == rv); + CU_ASSERT(0 == vc.version); + CU_ASSERT(&buf[1] == vc.dcid); + CU_ASSERT(NGTCP2_MAX_CIDLEN == vc.dcidlen); + CU_ASSERT(NULL == vc.scid); + CU_ASSERT(0 == vc.scidlen); + + /* Malformed Short packet */ + p = buf; + *p++ = 0; + p = ngtcp2_setmem(p, 0xf1, NGTCP2_MAX_CIDLEN); + --p; + + rv = ngtcp2_pkt_decode_version_cid(&vc, buf, (size_t)(p - buf), + NGTCP2_MAX_CIDLEN); + + CU_ASSERT(NGTCP2_ERR_INVALID_ARGUMENT == rv); +} + +void test_ngtcp2_pkt_decode_hd_long(void) { + ngtcp2_pkt_hd hd, nhd; + uint8_t buf[256]; + ngtcp2_ssize rv; + ngtcp2_cid dcid, scid; + size_t len; + + dcid_init(&dcid); + scid_init(&scid); + + /* Handshake */ + ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_LONG_FORM, NGTCP2_PKT_HANDSHAKE, + &dcid, &scid, 0xe1e2e3e4u, 4, NGTCP2_PROTO_VER_V1, 16383); + + rv = ngtcp2_pkt_encode_hd_long(buf, sizeof(buf), &hd); + + len = 1 + 4 + 1 + dcid.datalen + 1 + scid.datalen + NGTCP2_PKT_LENGTHLEN + 4; + + CU_ASSERT((ngtcp2_ssize)len == rv); + CU_ASSERT(buf[0] & NGTCP2_FIXED_BIT_MASK); + + rv = pkt_decode_hd_long(&nhd, buf, len); + + CU_ASSERT((ngtcp2_ssize)len == rv); + CU_ASSERT(hd.type == nhd.type); + CU_ASSERT(hd.flags == nhd.flags); + CU_ASSERT(ngtcp2_cid_eq(&hd.dcid, &nhd.dcid)); + CU_ASSERT(ngtcp2_cid_eq(&hd.scid, &nhd.scid)); + CU_ASSERT(0xe1e2e3e4u == nhd.pkt_num); + CU_ASSERT(hd.version == nhd.version); + CU_ASSERT(hd.len == nhd.len); + + /* Handshake without Fixed Bit set */ + ngtcp2_pkt_hd_init( + &hd, NGTCP2_PKT_FLAG_LONG_FORM | NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR, + NGTCP2_PKT_HANDSHAKE, &dcid, &scid, 0xe1e2e3e4u, 4, NGTCP2_PROTO_VER_V1, + 16383); + + rv = ngtcp2_pkt_encode_hd_long(buf, sizeof(buf), &hd); + + len = 1 + 4 + 1 + dcid.datalen + 1 + scid.datalen + NGTCP2_PKT_LENGTHLEN + 4; + + CU_ASSERT((ngtcp2_ssize)len == rv); + CU_ASSERT((buf[0] & NGTCP2_FIXED_BIT_MASK) == 0); + + rv = pkt_decode_hd_long(&nhd, buf, len); + + CU_ASSERT((ngtcp2_ssize)len == rv); + CU_ASSERT(hd.type == nhd.type); + CU_ASSERT(hd.flags == nhd.flags); + CU_ASSERT(ngtcp2_cid_eq(&hd.dcid, &nhd.dcid)); + CU_ASSERT(ngtcp2_cid_eq(&hd.scid, &nhd.scid)); + CU_ASSERT(0xe1e2e3e4u == nhd.pkt_num); + CU_ASSERT(hd.version == nhd.version); + CU_ASSERT(hd.len == nhd.len); + + /* VN */ + /* Set random packet type */ + ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_LONG_FORM, NGTCP2_PKT_HANDSHAKE, + &dcid, &scid, 0, 4, NGTCP2_PROTO_VER_V1, 0); + + rv = ngtcp2_pkt_encode_hd_long(buf, sizeof(buf), &hd); + /* Set version field to 0 */ + memset(&buf[1], 0, 4); + + len = 1 + 4 + 1 + dcid.datalen + 1 + scid.datalen; + + CU_ASSERT((ngtcp2_ssize)len == rv - NGTCP2_PKT_LENGTHLEN - 4 /* pkt_num */); + + rv = pkt_decode_hd_long(&nhd, buf, len); + + CU_ASSERT((ngtcp2_ssize)len == rv); + CU_ASSERT(NGTCP2_PKT_VERSION_NEGOTIATION == nhd.type); + CU_ASSERT((hd.flags & ~NGTCP2_PKT_FLAG_LONG_FORM) == nhd.flags); + CU_ASSERT(ngtcp2_cid_eq(&hd.dcid, &nhd.dcid)); + CU_ASSERT(ngtcp2_cid_eq(&hd.scid, &nhd.scid)); + CU_ASSERT(hd.pkt_num == nhd.pkt_num); + CU_ASSERT(0 == nhd.version); + CU_ASSERT(hd.len == nhd.len); +} + +void test_ngtcp2_pkt_decode_hd_short(void) { + ngtcp2_pkt_hd hd, nhd; + uint8_t buf[256]; + ngtcp2_ssize rv; + size_t expectedlen; + ngtcp2_cid dcid, zcid; + + dcid_init(&dcid); + ngtcp2_cid_zero(&zcid); + + /* 4 bytes packet number */ + ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_1RTT, &dcid, NULL, + 0xe1e2e3e4u, 4, 0xd1d2d3d4u, 0); + + expectedlen = 1 + dcid.datalen + 4; + + rv = ngtcp2_pkt_encode_hd_short(buf, sizeof(buf), &hd); + + CU_ASSERT((ngtcp2_ssize)expectedlen == rv); + CU_ASSERT(buf[0] & NGTCP2_FIXED_BIT_MASK); + + rv = pkt_decode_hd_short(&nhd, buf, expectedlen, dcid.datalen); + + CU_ASSERT((ngtcp2_ssize)expectedlen == rv); + CU_ASSERT(hd.flags == nhd.flags); + CU_ASSERT(NGTCP2_PKT_1RTT == nhd.type); + CU_ASSERT(ngtcp2_cid_eq(&dcid, &nhd.dcid)); + CU_ASSERT(ngtcp2_cid_empty(&nhd.scid)); + CU_ASSERT(0xe1e2e3e4u == nhd.pkt_num); + CU_ASSERT(hd.pkt_numlen == nhd.pkt_numlen); + CU_ASSERT(0 == nhd.version); + CU_ASSERT(0 == nhd.len); + + /* 4 bytes packet number without Fixed Bit set */ + ngtcp2_pkt_hd_init( + &hd, NGTCP2_PKT_FLAG_NONE | NGTCP2_PKT_FLAG_FIXED_BIT_CLEAR, + NGTCP2_PKT_1RTT, &dcid, NULL, 0xe1e2e3e4u, 4, 0xd1d2d3d4u, 0); + + expectedlen = 1 + dcid.datalen + 4; + + rv = ngtcp2_pkt_encode_hd_short(buf, sizeof(buf), &hd); + + CU_ASSERT((ngtcp2_ssize)expectedlen == rv); + CU_ASSERT((buf[0] & NGTCP2_FIXED_BIT_MASK) == 0); + + rv = pkt_decode_hd_short(&nhd, buf, expectedlen, dcid.datalen); + + CU_ASSERT((ngtcp2_ssize)expectedlen == rv); + CU_ASSERT(hd.flags == nhd.flags); + CU_ASSERT(NGTCP2_PKT_1RTT == nhd.type); + CU_ASSERT(ngtcp2_cid_eq(&dcid, &nhd.dcid)); + CU_ASSERT(ngtcp2_cid_empty(&nhd.scid)); + CU_ASSERT(0xe1e2e3e4u == nhd.pkt_num); + CU_ASSERT(hd.pkt_numlen == nhd.pkt_numlen); + CU_ASSERT(0 == nhd.version); + CU_ASSERT(0 == nhd.len); + + /* 2 bytes packet number */ + ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_1RTT, &dcid, NULL, + 0xe1e2e3e4u, 2, 0xd1d2d3d4u, 0); + + expectedlen = 1 + dcid.datalen + 2; + + rv = ngtcp2_pkt_encode_hd_short(buf, sizeof(buf), &hd); + + CU_ASSERT((ngtcp2_ssize)expectedlen == rv); + + rv = pkt_decode_hd_short(&nhd, buf, expectedlen, dcid.datalen); + + CU_ASSERT((ngtcp2_ssize)expectedlen == rv); + CU_ASSERT(hd.flags == nhd.flags); + CU_ASSERT(NGTCP2_PKT_1RTT == nhd.type); + CU_ASSERT(ngtcp2_cid_eq(&dcid, &nhd.dcid)); + CU_ASSERT(ngtcp2_cid_empty(&nhd.scid)); + CU_ASSERT(0xe3e4u == nhd.pkt_num); + CU_ASSERT(hd.pkt_numlen == nhd.pkt_numlen); + CU_ASSERT(0 == nhd.version); + CU_ASSERT(0 == nhd.len); + + /* 1 byte packet number */ + ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_1RTT, &dcid, NULL, + 0xe1e2e3e4u, 1, 0xd1d2d3d4u, 0); + + expectedlen = 1 + dcid.datalen + 1; + + rv = ngtcp2_pkt_encode_hd_short(buf, sizeof(buf), &hd); + + CU_ASSERT((ngtcp2_ssize)expectedlen == rv); + + rv = pkt_decode_hd_short(&nhd, buf, expectedlen, dcid.datalen); + + CU_ASSERT((ngtcp2_ssize)expectedlen == rv); + CU_ASSERT(hd.flags == nhd.flags); + CU_ASSERT(NGTCP2_PKT_1RTT == nhd.type); + CU_ASSERT(ngtcp2_cid_eq(&dcid, &nhd.dcid)); + CU_ASSERT(ngtcp2_cid_empty(&nhd.scid)); + CU_ASSERT(0xe4 == nhd.pkt_num); + CU_ASSERT(hd.pkt_numlen == nhd.pkt_numlen); + CU_ASSERT(0 == nhd.version); + CU_ASSERT(0 == nhd.len); + + /* With Key Phase */ + ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_KEY_PHASE, NGTCP2_PKT_1RTT, &dcid, + NULL, 0xe1e2e3e4u, 4, 0xd1d2d3d4u, 0); + + expectedlen = 1 + dcid.datalen + 4; + + rv = ngtcp2_pkt_encode_hd_short(buf, sizeof(buf), &hd); + + CU_ASSERT((ngtcp2_ssize)expectedlen == rv); + + rv = pkt_decode_hd_short(&nhd, buf, expectedlen, dcid.datalen); + + CU_ASSERT((ngtcp2_ssize)expectedlen == rv); + /* key phase bit is protected by header protection and + ngtcp2_pkt_decode_hd_short does not decode it. */ + CU_ASSERT(NGTCP2_PKT_FLAG_NONE == nhd.flags); + CU_ASSERT(NGTCP2_PKT_1RTT == nhd.type); + CU_ASSERT(ngtcp2_cid_eq(&dcid, &nhd.dcid)); + CU_ASSERT(ngtcp2_cid_empty(&nhd.scid)); + CU_ASSERT(0xe1e2e3e4u == nhd.pkt_num); + CU_ASSERT(hd.pkt_numlen == nhd.pkt_numlen); + CU_ASSERT(0 == nhd.version); + CU_ASSERT(0 == nhd.len); + + /* With empty DCID */ + ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_1RTT, NULL, NULL, + 0xe1e2e3e4u, 4, 0xd1d2d3d4u, 0); + + expectedlen = 1 + 4; + + rv = ngtcp2_pkt_encode_hd_short(buf, sizeof(buf), &hd); + + CU_ASSERT((ngtcp2_ssize)expectedlen == rv); + + rv = pkt_decode_hd_short(&nhd, buf, expectedlen, 0); + + CU_ASSERT((ngtcp2_ssize)expectedlen == rv); + CU_ASSERT(hd.flags == nhd.flags); + CU_ASSERT(NGTCP2_PKT_1RTT == nhd.type); + CU_ASSERT(ngtcp2_cid_empty(&nhd.dcid)); + CU_ASSERT(ngtcp2_cid_empty(&nhd.scid)); + CU_ASSERT(0xe1e2e3e4u == nhd.pkt_num); + CU_ASSERT(hd.pkt_numlen == nhd.pkt_numlen); + CU_ASSERT(0 == nhd.version); + CU_ASSERT(0 == nhd.len); +} + +void test_ngtcp2_pkt_decode_stream_frame(void) { + uint8_t buf[256]; + size_t buflen; + ngtcp2_frame fr; + ngtcp2_ssize rv; + size_t expectedlen; + + /* 32 bits Stream ID + 62 bits Offset + Data Length */ + buflen = ngtcp2_t_encode_stream_frame(buf, NGTCP2_STREAM_LEN_BIT, 0xf1f2f3f4u, + 0x31f2f3f4f5f6f7f8llu, 0x14); + + expectedlen = 1 + 8 + 8 + 1 + 20; + + CU_ASSERT(expectedlen == buflen); + + rv = ngtcp2_pkt_decode_stream_frame(&fr.stream, buf, buflen); + + CU_ASSERT((ngtcp2_ssize)expectedlen == rv); + CU_ASSERT(0 == fr.stream.fin); + CU_ASSERT(0xf1f2f3f4u == fr.stream.stream_id); + CU_ASSERT(0x31f2f3f4f5f6f7f8llu == fr.stream.offset); + CU_ASSERT(1 == fr.stream.datacnt); + CU_ASSERT(0x14 == fr.stream.data[0].len); + + /* Cutting 1 bytes from the tail must cause invalid argument + error */ + rv = ngtcp2_pkt_decode_stream_frame(&fr.stream, buf, buflen - 1); + + CU_ASSERT(NGTCP2_ERR_FRAME_ENCODING == rv); + + memset(&fr, 0, sizeof(fr)); + + /* 6 bits Stream ID + no Offset + Data Length */ + buflen = ngtcp2_t_encode_stream_frame(buf, NGTCP2_STREAM_LEN_BIT, 0x31, 0x00, + 0x14); + + expectedlen = 1 + 1 + 0 + 1 + 20; + + CU_ASSERT(expectedlen == buflen); + + rv = ngtcp2_pkt_decode_stream_frame(&fr.stream, buf, buflen); + + CU_ASSERT((ngtcp2_ssize)expectedlen == rv); + CU_ASSERT(0 == fr.stream.fin); + CU_ASSERT(0x31 == fr.stream.stream_id); + CU_ASSERT(0x00 == fr.stream.offset); + CU_ASSERT(1 == fr.stream.datacnt); + CU_ASSERT(0x14 == fr.stream.data[0].len); + + /* Cutting 1 bytes from the tail must cause invalid argument + error */ + rv = ngtcp2_pkt_decode_stream_frame(&fr.stream, buf, buflen - 1); + + CU_ASSERT(NGTCP2_ERR_FRAME_ENCODING == rv); + + memset(&fr, 0, sizeof(fr)); + + /* Fin bit set + no Data Length */ + buflen = ngtcp2_t_encode_stream_frame(buf, NGTCP2_STREAM_FIN_BIT, 0x31f2f3f4u, + 0x00, 0x14); + + expectedlen = 1 + 4 + 20; + + CU_ASSERT(expectedlen == buflen); + + rv = ngtcp2_pkt_decode_stream_frame(&fr.stream, buf, buflen); + + CU_ASSERT((ngtcp2_ssize)expectedlen == rv); + CU_ASSERT(1 == fr.stream.fin); + CU_ASSERT(0x31f2f3f4u == fr.stream.stream_id); + CU_ASSERT(0x00 == fr.stream.offset); + CU_ASSERT(1 == fr.stream.datacnt); + CU_ASSERT(0x14 == fr.stream.data[0].len); + + memset(&fr, 0, sizeof(fr)); +} + +void test_ngtcp2_pkt_decode_ack_frame(void) { + uint8_t buf[256]; + size_t buflen; + ngtcp2_frame fr; + ngtcp2_ssize rv; + size_t expectedlen; + + /* 62 bits Largest Acknowledged */ + buflen = ngtcp2_t_encode_ack_frame(buf, 0x31f2f3f4f5f6f7f8llu, + 0x31e2e3e4e5e6e7e8llu, 99, + 0x31d2d3d4d5d6d7d8llu); + + expectedlen = 1 + 8 + 1 + 1 + 8 + 2 + 8; + + CU_ASSERT(expectedlen == buflen); + + rv = ngtcp2_pkt_decode_ack_frame(&fr.ack, buf, buflen); + + CU_ASSERT((ngtcp2_ssize)expectedlen == rv); + CU_ASSERT(0x31f2f3f4f5f6f7f8llu == fr.ack.largest_ack); + CU_ASSERT(1 == fr.ack.rangecnt); + CU_ASSERT(0x31e2e3e4e5e6e7e8llu == fr.ack.first_ack_range); + CU_ASSERT(99 == fr.ack.ranges[0].gap); + CU_ASSERT(0x31d2d3d4d5d6d7d8llu == fr.ack.ranges[0].len); +} + +void test_ngtcp2_pkt_decode_padding_frame(void) { + uint8_t buf[256]; + ngtcp2_frame fr; + ngtcp2_ssize rv; + size_t paddinglen = 31; + + memset(buf, 0, paddinglen); + buf[paddinglen] = NGTCP2_FRAME_STREAM; + + rv = ngtcp2_pkt_decode_padding_frame(&fr.padding, buf, paddinglen + 1); + + CU_ASSERT((ngtcp2_ssize)paddinglen == rv); + CU_ASSERT((size_t)31 == fr.padding.len); +} + +void test_ngtcp2_pkt_encode_stream_frame(void) { + const uint8_t data[] = "0123456789abcdef0"; + uint8_t buf[256]; + ngtcp2_frame fr, nfr; + ngtcp2_ssize rv; + size_t framelen; + size_t i; + + /* 32 bits Stream ID + 62 bits Offset + Data Length */ + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.fin = 0; + fr.stream.stream_id = 0xf1f2f3f4u; + fr.stream.offset = 0x31f2f3f4f5f6f7f8llu; + fr.stream.datacnt = 1; + fr.stream.data[0].len = strsize(data); + fr.stream.data[0].base = (uint8_t *)data; + + framelen = 1 + 8 + 8 + 1 + 17; + + rv = ngtcp2_pkt_encode_stream_frame(buf, sizeof(buf), &fr.stream); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + + rv = ngtcp2_pkt_decode_stream_frame(&nfr.stream, buf, framelen); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + CU_ASSERT(fr.type == nfr.type); + CU_ASSERT((NGTCP2_STREAM_OFF_BIT | NGTCP2_STREAM_LEN_BIT) == + nfr.stream.flags); + CU_ASSERT(fr.stream.fin == nfr.stream.fin); + CU_ASSERT(fr.stream.stream_id == nfr.stream.stream_id); + CU_ASSERT(fr.stream.offset == nfr.stream.offset); + CU_ASSERT(1 == nfr.stream.datacnt); + CU_ASSERT(fr.stream.data[0].len == nfr.stream.data[0].len); + CU_ASSERT(0 == memcmp(fr.stream.data[0].base, nfr.stream.data[0].base, + fr.stream.data[0].len)); + + memset(&nfr, 0, sizeof(nfr)); + + /* 6 bits Stream ID + No Offset + Data Length */ + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.fin = 0; + fr.stream.stream_id = 0x31; + fr.stream.offset = 0; + fr.stream.datacnt = 1; + fr.stream.data[0].len = strsize(data); + fr.stream.data[0].base = (uint8_t *)data; + + framelen = 1 + 1 + 1 + 17; + + rv = ngtcp2_pkt_encode_stream_frame(buf, sizeof(buf), &fr.stream); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + + rv = ngtcp2_pkt_decode_stream_frame(&nfr.stream, buf, framelen); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + CU_ASSERT(fr.type == nfr.type); + CU_ASSERT(NGTCP2_STREAM_LEN_BIT == nfr.stream.flags); + CU_ASSERT(fr.stream.fin == nfr.stream.fin); + CU_ASSERT(fr.stream.stream_id == nfr.stream.stream_id); + CU_ASSERT(fr.stream.offset == nfr.stream.offset); + CU_ASSERT(1 == nfr.stream.datacnt); + CU_ASSERT(fr.stream.data[0].len == nfr.stream.data[0].len); + CU_ASSERT(0 == memcmp(fr.stream.data[0].base, nfr.stream.data[0].base, + fr.stream.data[0].len)); + + memset(&nfr, 0, sizeof(nfr)); + + /* Fin + 32 bits Stream ID + 62 bits Offset + Data Length */ + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.fin = 1; + fr.stream.stream_id = 0xf1f2f3f4u; + fr.stream.offset = 0x31f2f3f4f5f6f7f8llu; + fr.stream.datacnt = 1; + fr.stream.data[0].len = strsize(data); + fr.stream.data[0].base = (uint8_t *)data; + + framelen = 1 + 8 + 8 + 1 + 17; + + rv = ngtcp2_pkt_encode_stream_frame(buf, sizeof(buf), &fr.stream); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + + rv = ngtcp2_pkt_decode_stream_frame(&nfr.stream, buf, framelen); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + CU_ASSERT(fr.type == nfr.type); + CU_ASSERT((NGTCP2_STREAM_FIN_BIT | NGTCP2_STREAM_OFF_BIT | + NGTCP2_STREAM_LEN_BIT) == nfr.stream.flags); + CU_ASSERT(fr.stream.fin == nfr.stream.fin); + CU_ASSERT(fr.stream.stream_id == nfr.stream.stream_id); + CU_ASSERT(fr.stream.offset == nfr.stream.offset); + CU_ASSERT(1 == nfr.stream.datacnt); + CU_ASSERT(fr.stream.data[0].len == nfr.stream.data[0].len); + CU_ASSERT(0 == memcmp(fr.stream.data[0].base, nfr.stream.data[0].base, + fr.stream.data[0].len)); + + /* Make sure that we check the length properly. */ + for (i = 1; i < framelen; ++i) { + rv = ngtcp2_pkt_decode_stream_frame(&nfr.stream, buf, i); + + CU_ASSERT(NGTCP2_ERR_FRAME_ENCODING == rv); + } + + memset(&nfr, 0, sizeof(nfr)); + + /* NOBUF: Fin + 32 bits Stream ID + 62 bits Offset + Data Length */ + fr.type = NGTCP2_FRAME_STREAM; + fr.stream.fin = 1; + fr.stream.stream_id = 0xf1f2f3f4u; + fr.stream.offset = 0x31f2f3f4f5f6f7f8llu; + fr.stream.datacnt = 1; + fr.stream.data[0].len = strsize(data); + fr.stream.data[0].base = (uint8_t *)data; + + framelen = 1 + 8 + 8 + 1 + 17; + + rv = ngtcp2_pkt_encode_stream_frame(buf, framelen - 1, &fr.stream); + + CU_ASSERT(NGTCP2_ERR_NOBUF == rv); +} + +void test_ngtcp2_pkt_encode_ack_frame(void) { + uint8_t buf[256]; + ngtcp2_max_frame mfr, nmfr; + ngtcp2_frame *fr = &mfr.fr, *nfr = &nmfr.fr; + ngtcp2_ssize rv; + size_t framelen; + size_t i; + ngtcp2_ack_range *ranges; + + /* 0 Num Blocks */ + fr->type = NGTCP2_FRAME_ACK; + fr->ack.largest_ack = 0xf1f2f3f4llu; + fr->ack.first_ack_range = 0; + fr->ack.ack_delay = 0; + fr->ack.rangecnt = 0; + + framelen = 1 + 8 + 1 + 1 + 1; + + rv = ngtcp2_pkt_encode_ack_frame(buf, sizeof(buf), &fr->ack); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + + rv = ngtcp2_pkt_decode_ack_frame(&nfr->ack, buf, framelen); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + CU_ASSERT(fr->type == nfr->type); + CU_ASSERT(fr->ack.largest_ack == nfr->ack.largest_ack); + CU_ASSERT(fr->ack.ack_delay == nfr->ack.ack_delay); + CU_ASSERT(fr->ack.rangecnt == nfr->ack.rangecnt); + + memset(&nmfr, 0, sizeof(nmfr)); + + /* 2 Num Blocks */ + fr->type = NGTCP2_FRAME_ACK; + fr->ack.largest_ack = 0xf1f2f3f4llu; + fr->ack.first_ack_range = 0xe1e2e3e4llu; + fr->ack.ack_delay = 0xf1f2; + fr->ack.rangecnt = 2; + ranges = fr->ack.ranges; + ranges[0].gap = 255; + ranges[0].len = 0xd1d2d3d4llu; + ranges[1].gap = 1; + ranges[1].len = 0xd1d2d3d4llu; + + framelen = 1 + 8 + 4 + 1 + 8 + (2 + 8) + (1 + 8); + + rv = ngtcp2_pkt_encode_ack_frame(buf, sizeof(buf), &fr->ack); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + + rv = ngtcp2_pkt_decode_ack_frame(&nfr->ack, buf, framelen); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + CU_ASSERT(fr->type == nfr->type); + CU_ASSERT(fr->ack.largest_ack == nfr->ack.largest_ack); + CU_ASSERT(fr->ack.ack_delay == nfr->ack.ack_delay); + CU_ASSERT(fr->ack.rangecnt == nfr->ack.rangecnt); + + for (i = 0; i < fr->ack.rangecnt; ++i) { + CU_ASSERT(fr->ack.ranges[i].gap == nfr->ack.ranges[i].gap); + CU_ASSERT(fr->ack.ranges[i].len == nfr->ack.ranges[i].len); + } + + memset(&nmfr, 0, sizeof(nmfr)); +} + +void test_ngtcp2_pkt_encode_ack_ecn_frame(void) { + uint8_t buf[256]; + ngtcp2_max_frame mfr, nmfr; + ngtcp2_frame *fr = &mfr.fr, *nfr = &nmfr.fr; + ngtcp2_ssize rv; + size_t framelen; + size_t i; + ngtcp2_ack_range *ranges; + + /* 0 Num Blocks */ + fr->type = NGTCP2_FRAME_ACK_ECN; + fr->ack.largest_ack = 0xf1f2f3f4llu; + fr->ack.first_ack_range = 0; + fr->ack.ack_delay = 0; + fr->ack.rangecnt = 0; + fr->ack.ecn.ect0 = 64; + fr->ack.ecn.ect1 = 16384; + fr->ack.ecn.ce = 1073741824; + + framelen = 1 + 8 + 1 + 1 + 1 + 2 + 4 + 8; + + rv = ngtcp2_pkt_encode_ack_frame(buf, sizeof(buf), &fr->ack); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + + rv = ngtcp2_pkt_decode_ack_frame(&nfr->ack, buf, framelen); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + CU_ASSERT(fr->type == nfr->type); + CU_ASSERT(fr->ack.largest_ack == nfr->ack.largest_ack); + CU_ASSERT(fr->ack.ack_delay == nfr->ack.ack_delay); + CU_ASSERT(fr->ack.rangecnt == nfr->ack.rangecnt); + CU_ASSERT(fr->ack.ecn.ect0 == nfr->ack.ecn.ect0); + CU_ASSERT(fr->ack.ecn.ect1 == nfr->ack.ecn.ect1); + CU_ASSERT(fr->ack.ecn.ce == nfr->ack.ecn.ce); + + memset(&nmfr, 0, sizeof(nmfr)); + + /* 2 Num Blocks */ + fr->type = NGTCP2_FRAME_ACK_ECN; + fr->ack.largest_ack = 0xf1f2f3f4llu; + fr->ack.first_ack_range = 0xe1e2e3e4llu; + fr->ack.ack_delay = 0xf1f2; + fr->ack.rangecnt = 2; + ranges = fr->ack.ranges; + ranges[0].gap = 255; + ranges[0].len = 0xd1d2d3d4llu; + ranges[1].gap = 1; + ranges[1].len = 0xd1d2d3d4llu; + fr->ack.ecn.ect0 = 0; + fr->ack.ecn.ect1 = 64; + fr->ack.ecn.ce = 16384; + + framelen = 1 + 8 + 4 + 1 + 8 + (2 + 8) + (1 + 8) + 1 + 2 + 4; + + rv = ngtcp2_pkt_encode_ack_frame(buf, sizeof(buf), &fr->ack); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + + rv = ngtcp2_pkt_decode_ack_frame(&nfr->ack, buf, framelen); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + CU_ASSERT(fr->type == nfr->type); + CU_ASSERT(fr->ack.largest_ack == nfr->ack.largest_ack); + CU_ASSERT(fr->ack.ack_delay == nfr->ack.ack_delay); + CU_ASSERT(fr->ack.rangecnt == nfr->ack.rangecnt); + + for (i = 0; i < fr->ack.rangecnt; ++i) { + CU_ASSERT(fr->ack.ranges[i].gap == nfr->ack.ranges[i].gap); + CU_ASSERT(fr->ack.ranges[i].len == nfr->ack.ranges[i].len); + } + + CU_ASSERT(fr->ack.ecn.ect0 == nfr->ack.ecn.ect0); + CU_ASSERT(fr->ack.ecn.ect1 == nfr->ack.ecn.ect1); + CU_ASSERT(fr->ack.ecn.ce == nfr->ack.ecn.ce); + + memset(&nmfr, 0, sizeof(nmfr)); +} + +void test_ngtcp2_pkt_encode_reset_stream_frame(void) { + uint8_t buf[32]; + ngtcp2_reset_stream fr, nfr; + ngtcp2_ssize rv; + size_t framelen = 1 + 4 + 4 + 8; + + fr.type = NGTCP2_FRAME_RESET_STREAM; + fr.stream_id = 1000000007; + fr.app_error_code = 0xe1e2; + fr.final_size = 0x31f2f3f4f5f6f7f8llu; + + rv = ngtcp2_pkt_encode_reset_stream_frame(buf, sizeof(buf), &fr); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + + rv = ngtcp2_pkt_decode_reset_stream_frame(&nfr, buf, framelen); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + CU_ASSERT(fr.type == nfr.type); + CU_ASSERT(fr.stream_id == nfr.stream_id); + CU_ASSERT(fr.app_error_code == nfr.app_error_code); + CU_ASSERT(fr.final_size == nfr.final_size); +} + +void test_ngtcp2_pkt_encode_connection_close_frame(void) { + uint8_t buf[2048]; + ngtcp2_frame fr, nfr; + ngtcp2_ssize rv; + size_t framelen; + uint8_t reason[1024]; + + memset(reason, 0xfa, sizeof(reason)); + + /* no Reason Phrase */ + fr.type = NGTCP2_FRAME_CONNECTION_CLOSE; + fr.connection_close.error_code = 0xf1f2u; + fr.connection_close.frame_type = 255; + fr.connection_close.reasonlen = 0; + fr.connection_close.reason = NULL; + + framelen = 1 + 4 + 2 + 1; + + rv = ngtcp2_pkt_encode_connection_close_frame(buf, sizeof(buf), + &fr.connection_close); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + + rv = ngtcp2_pkt_decode_connection_close_frame(&nfr.connection_close, buf, + framelen); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + CU_ASSERT(fr.type == nfr.type); + CU_ASSERT(fr.connection_close.error_code == nfr.connection_close.error_code); + CU_ASSERT(fr.connection_close.reasonlen == nfr.connection_close.reasonlen); + CU_ASSERT(fr.connection_close.reason == nfr.connection_close.reason); + + memset(&nfr, 0, sizeof(nfr)); + + /* 1024 bytes Reason Phrase */ + fr.type = NGTCP2_FRAME_CONNECTION_CLOSE; + fr.connection_close.error_code = 0xf3f4u; + fr.connection_close.frame_type = 0; + fr.connection_close.reasonlen = sizeof(reason); + fr.connection_close.reason = reason; + + framelen = 1 + 4 + 1 + 2 + sizeof(reason); + + rv = ngtcp2_pkt_encode_connection_close_frame(buf, sizeof(buf), + &fr.connection_close); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + + rv = ngtcp2_pkt_decode_connection_close_frame(&nfr.connection_close, buf, + framelen); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + CU_ASSERT(fr.type == nfr.type); + CU_ASSERT(fr.connection_close.error_code == nfr.connection_close.error_code); + CU_ASSERT(fr.connection_close.reasonlen == nfr.connection_close.reasonlen); + CU_ASSERT(0 == memcmp(reason, nfr.connection_close.reason, sizeof(reason))); + + memset(&nfr, 0, sizeof(nfr)); +} + +void test_ngtcp2_pkt_encode_connection_close_app_frame(void) { + uint8_t buf[2048]; + ngtcp2_frame fr, nfr; + ngtcp2_ssize rv; + size_t framelen; + uint8_t reason[1024]; + + memset(reason, 0xfa, sizeof(reason)); + + /* no Reason Phrase */ + fr.type = NGTCP2_FRAME_CONNECTION_CLOSE_APP; + fr.connection_close.error_code = 0xf1f2u; + fr.connection_close.frame_type = 0xff; /* This must be ignored. */ + fr.connection_close.reasonlen = 0; + fr.connection_close.reason = NULL; + + framelen = 1 + 4 + 1; + + rv = ngtcp2_pkt_encode_connection_close_frame(buf, sizeof(buf), + &fr.connection_close); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + + rv = ngtcp2_pkt_decode_connection_close_frame(&nfr.connection_close, buf, + framelen); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + CU_ASSERT(fr.type == nfr.type); + CU_ASSERT(fr.connection_close.error_code == nfr.connection_close.error_code); + CU_ASSERT(0 == nfr.connection_close.frame_type); + CU_ASSERT(fr.connection_close.reasonlen == nfr.connection_close.reasonlen); + CU_ASSERT(fr.connection_close.reason == nfr.connection_close.reason); + + memset(&nfr, 0, sizeof(nfr)); +} + +void test_ngtcp2_pkt_encode_max_data_frame(void) { + uint8_t buf[16]; + ngtcp2_max_data fr, nfr; + ngtcp2_ssize rv; + size_t framelen = 1 + 8; + + fr.type = NGTCP2_FRAME_MAX_DATA; + fr.max_data = 0x31f2f3f4f5f6f7f8llu; + + rv = ngtcp2_pkt_encode_max_data_frame(buf, sizeof(buf), &fr); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + + rv = ngtcp2_pkt_decode_max_data_frame(&nfr, buf, framelen); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + CU_ASSERT(fr.type == nfr.type); + CU_ASSERT(fr.max_data == nfr.max_data); +} + +void test_ngtcp2_pkt_encode_max_stream_data_frame(void) { + uint8_t buf[17]; + ngtcp2_max_stream_data fr, nfr; + ngtcp2_ssize rv; + size_t framelen = 1 + 8 + 8; + + fr.type = NGTCP2_FRAME_MAX_STREAM_DATA; + fr.stream_id = 0xf1f2f3f4u; + fr.max_stream_data = 0x35f6f7f8f9fafbfcllu; + + rv = ngtcp2_pkt_encode_max_stream_data_frame(buf, sizeof(buf), &fr); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + + rv = ngtcp2_pkt_decode_max_stream_data_frame(&nfr, buf, framelen); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + CU_ASSERT(fr.type == nfr.type); + CU_ASSERT(fr.stream_id == nfr.stream_id); + CU_ASSERT(fr.max_stream_data == nfr.max_stream_data); +} + +void test_ngtcp2_pkt_encode_max_streams_frame(void) { + uint8_t buf[16]; + ngtcp2_max_streams fr, nfr; + ngtcp2_ssize rv; + size_t framelen = 1 + 8; + + fr.type = NGTCP2_FRAME_MAX_STREAMS_BIDI; + fr.max_streams = 0xf1f2f3f4u; + + rv = ngtcp2_pkt_encode_max_streams_frame(buf, sizeof(buf), &fr); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + + rv = ngtcp2_pkt_decode_max_streams_frame(&nfr, buf, framelen); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + CU_ASSERT(fr.type == nfr.type); + CU_ASSERT(fr.max_streams == nfr.max_streams); +} + +void test_ngtcp2_pkt_encode_ping_frame(void) { + uint8_t buf[3]; + ngtcp2_ping fr, nfr; + ngtcp2_ssize rv; + size_t framelen; + + fr.type = NGTCP2_FRAME_PING; + + framelen = 1; + + rv = ngtcp2_pkt_encode_ping_frame(buf, sizeof(buf), &fr); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + + rv = ngtcp2_pkt_decode_ping_frame(&nfr, buf, framelen); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + CU_ASSERT(fr.type == nfr.type); +} + +void test_ngtcp2_pkt_encode_data_blocked_frame(void) { + uint8_t buf[9]; + ngtcp2_data_blocked fr, nfr; + ngtcp2_ssize rv; + size_t framelen = 1 + 8; + + fr.type = NGTCP2_FRAME_DATA_BLOCKED; + fr.offset = 0x31f2f3f4f5f6f7f8llu; + + rv = ngtcp2_pkt_encode_data_blocked_frame(buf, sizeof(buf), &fr); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + + rv = ngtcp2_pkt_decode_data_blocked_frame(&nfr, buf, framelen); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + CU_ASSERT(fr.type == nfr.type); + CU_ASSERT(fr.offset == nfr.offset); +} + +void test_ngtcp2_pkt_encode_stream_data_blocked_frame(void) { + uint8_t buf[17]; + ngtcp2_stream_data_blocked fr, nfr; + ngtcp2_ssize rv; + size_t framelen = 1 + 8 + 8; + + fr.type = NGTCP2_FRAME_STREAM_DATA_BLOCKED; + fr.stream_id = 0xf1f2f3f4u; + fr.offset = 0x35f6f7f8f9fafbfcllu; + + rv = ngtcp2_pkt_encode_stream_data_blocked_frame(buf, sizeof(buf), &fr); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + + rv = ngtcp2_pkt_decode_stream_data_blocked_frame(&nfr, buf, framelen); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + CU_ASSERT(fr.type == nfr.type); + CU_ASSERT(fr.stream_id == nfr.stream_id); + CU_ASSERT(fr.offset == nfr.offset); +} + +void test_ngtcp2_pkt_encode_streams_blocked_frame(void) { + uint8_t buf[9]; + ngtcp2_streams_blocked fr, nfr; + ngtcp2_ssize rv; + size_t framelen = 1 + 8; + + fr.type = NGTCP2_FRAME_STREAMS_BLOCKED_BIDI; + fr.max_streams = 0xf1f2f3f4u; + + rv = ngtcp2_pkt_encode_streams_blocked_frame(buf, sizeof(buf), &fr); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + + rv = ngtcp2_pkt_decode_streams_blocked_frame(&nfr, buf, framelen); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + CU_ASSERT(fr.type == nfr.type); + CU_ASSERT(fr.max_streams == nfr.max_streams); +} + +void test_ngtcp2_pkt_encode_new_connection_id_frame(void) { + uint8_t buf[256]; + ngtcp2_new_connection_id fr, nfr; + ngtcp2_ssize rv; + size_t framelen = 1 + 4 + 2 + 1 + 18 + NGTCP2_STATELESS_RESET_TOKENLEN; + + fr.type = NGTCP2_FRAME_NEW_CONNECTION_ID; + fr.seq = 1000000009; + fr.retire_prior_to = 255; + scid_init(&fr.cid); + memset(fr.stateless_reset_token, 0xe1, sizeof(fr.stateless_reset_token)); + + rv = ngtcp2_pkt_encode_new_connection_id_frame(buf, sizeof(buf), &fr); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + + rv = ngtcp2_pkt_decode_new_connection_id_frame(&nfr, buf, framelen); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + CU_ASSERT(fr.type == nfr.type); + CU_ASSERT(fr.seq == nfr.seq); + CU_ASSERT(ngtcp2_cid_eq(&fr.cid, &nfr.cid)); + CU_ASSERT(0 == memcmp(fr.stateless_reset_token, nfr.stateless_reset_token, + sizeof(fr.stateless_reset_token))); +} + +void test_ngtcp2_pkt_encode_stop_sending_frame(void) { + uint8_t buf[16]; + ngtcp2_stop_sending fr, nfr; + ngtcp2_ssize rv; + size_t framelen = 1 + 8 + 4; + + fr.type = NGTCP2_FRAME_STOP_SENDING; + fr.stream_id = 0xf1f2f3f4u; + fr.app_error_code = 0xe1e2u; + + rv = ngtcp2_pkt_encode_stop_sending_frame(buf, sizeof(buf), &fr); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + + rv = ngtcp2_pkt_decode_stop_sending_frame(&nfr, buf, framelen); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + CU_ASSERT(fr.type == nfr.type); + CU_ASSERT(fr.stream_id == nfr.stream_id); + CU_ASSERT(fr.app_error_code == nfr.app_error_code); +} + +void test_ngtcp2_pkt_encode_path_challenge_frame(void) { + uint8_t buf[9]; + ngtcp2_path_challenge fr, nfr; + ngtcp2_ssize rv; + size_t framelen = 1 + 8; + size_t i; + + fr.type = NGTCP2_FRAME_PATH_CHALLENGE; + for (i = 0; i < sizeof(fr.data); ++i) { + fr.data[i] = (uint8_t)(i + 1); + } + + rv = ngtcp2_pkt_encode_path_challenge_frame(buf, sizeof(buf), &fr); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + + rv = ngtcp2_pkt_decode_path_challenge_frame(&nfr, buf, framelen); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + CU_ASSERT(fr.type == nfr.type); + CU_ASSERT(0 == memcmp(fr.data, nfr.data, sizeof(fr.data))); +} + +void test_ngtcp2_pkt_encode_path_response_frame(void) { + uint8_t buf[9]; + ngtcp2_path_response fr, nfr; + ngtcp2_ssize rv; + size_t framelen = 1 + 8; + size_t i; + + fr.type = NGTCP2_FRAME_PATH_RESPONSE; + for (i = 0; i < sizeof(fr.data); ++i) { + fr.data[i] = (uint8_t)(i + 1); + } + + rv = ngtcp2_pkt_encode_path_response_frame(buf, sizeof(buf), &fr); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + + rv = ngtcp2_pkt_decode_path_response_frame(&nfr, buf, framelen); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + CU_ASSERT(fr.type == nfr.type); + CU_ASSERT(0 == memcmp(fr.data, nfr.data, sizeof(fr.data))); +} + +void test_ngtcp2_pkt_encode_crypto_frame(void) { + const uint8_t data[] = "0123456789abcdef1"; + uint8_t buf[256]; + ngtcp2_frame fr, nfr; + ngtcp2_ssize rv; + size_t framelen; + + fr.type = NGTCP2_FRAME_CRYPTO; + fr.crypto.offset = 0x31f2f3f4f5f6f7f8llu; + fr.crypto.datacnt = 1; + fr.crypto.data[0].len = strsize(data); + fr.crypto.data[0].base = (uint8_t *)data; + + framelen = 1 + 8 + 1 + 17; + + rv = ngtcp2_pkt_encode_crypto_frame(buf, sizeof(buf), &fr.crypto); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + + rv = ngtcp2_pkt_decode_crypto_frame(&nfr.crypto, buf, framelen); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + CU_ASSERT(fr.type == nfr.type); + CU_ASSERT(fr.crypto.offset == nfr.crypto.offset); + CU_ASSERT(fr.crypto.datacnt == nfr.crypto.datacnt); + CU_ASSERT(fr.crypto.data[0].len == nfr.crypto.data[0].len); + CU_ASSERT(0 == memcmp(fr.crypto.data[0].base, nfr.crypto.data[0].base, + fr.crypto.data[0].len)); +} + +void test_ngtcp2_pkt_encode_new_token_frame(void) { + const uint8_t token[] = "0123456789abcdef2"; + uint8_t buf[256]; + ngtcp2_frame fr, nfr; + ngtcp2_ssize rv; + size_t framelen; + + fr.type = NGTCP2_FRAME_NEW_TOKEN; + ngtcp2_vec_init(&fr.new_token.token, token, strsize(token)); + + framelen = 1 + 1 + strsize(token); + + rv = ngtcp2_pkt_encode_new_token_frame(buf, sizeof(buf), &fr.new_token); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + + rv = ngtcp2_pkt_decode_new_token_frame(&nfr.new_token, buf, framelen); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + CU_ASSERT(fr.type == nfr.type); + CU_ASSERT(fr.new_token.token.len == nfr.new_token.token.len); + CU_ASSERT(0 == memcmp(fr.new_token.token.base, nfr.new_token.token.base, + fr.new_token.token.len)); +} + +void test_ngtcp2_pkt_encode_retire_connection_id_frame(void) { + uint8_t buf[256]; + ngtcp2_frame fr, nfr; + ngtcp2_ssize rv; + size_t framelen; + + fr.type = NGTCP2_FRAME_RETIRE_CONNECTION_ID; + fr.retire_connection_id.seq = 1000000007; + + framelen = 1 + ngtcp2_put_uvarintlen(fr.retire_connection_id.seq); + + rv = ngtcp2_pkt_encode_retire_connection_id_frame(buf, sizeof(buf), + &fr.retire_connection_id); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + + rv = ngtcp2_pkt_decode_retire_connection_id_frame(&nfr.retire_connection_id, + buf, framelen); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + CU_ASSERT(fr.type == nfr.type); + CU_ASSERT(fr.retire_connection_id.seq == nfr.retire_connection_id.seq); +} + +void test_ngtcp2_pkt_encode_handshake_done_frame(void) { + uint8_t buf[16]; + ngtcp2_handshake_done fr, nfr; + ngtcp2_ssize rv; + size_t framelen = 1; + + fr.type = NGTCP2_FRAME_HANDSHAKE_DONE; + + rv = ngtcp2_pkt_encode_handshake_done_frame(buf, sizeof(buf), &fr); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + + rv = ngtcp2_pkt_decode_handshake_done_frame(&nfr, buf, framelen); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + CU_ASSERT(fr.type == nfr.type); +} + +void test_ngtcp2_pkt_encode_datagram_frame(void) { + const uint8_t data[] = "0123456789abcdef3"; + uint8_t buf[256]; + ngtcp2_frame fr, nfr; + ngtcp2_ssize rv; + size_t framelen; + + fr.type = NGTCP2_FRAME_DATAGRAM_LEN; + fr.datagram.datacnt = 1; + fr.datagram.data = fr.datagram.rdata; + fr.datagram.rdata[0].len = strsize(data); + fr.datagram.rdata[0].base = (uint8_t *)data; + + framelen = 1 + 1 + 17; + + rv = ngtcp2_pkt_encode_datagram_frame(buf, sizeof(buf), &fr.datagram); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + + rv = ngtcp2_pkt_decode_datagram_frame(&nfr.datagram, buf, framelen); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + CU_ASSERT(fr.type == nfr.type); + CU_ASSERT(fr.datagram.datacnt == nfr.datagram.datacnt); + CU_ASSERT(fr.datagram.data->len == nfr.datagram.data->len); + CU_ASSERT(0 == memcmp(fr.datagram.data->base, nfr.datagram.data->base, + fr.datagram.data->len)); + + memset(&nfr, 0, sizeof(nfr)); + + /* Without length field */ + fr.type = NGTCP2_FRAME_DATAGRAM; + fr.datagram.datacnt = 1; + fr.datagram.data = fr.datagram.rdata; + fr.datagram.rdata[0].len = strsize(data); + fr.datagram.rdata[0].base = (uint8_t *)data; + + framelen = 1 + 17; + + rv = ngtcp2_pkt_encode_datagram_frame(buf, sizeof(buf), &fr.datagram); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + + rv = ngtcp2_pkt_decode_datagram_frame(&nfr.datagram, buf, framelen); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + CU_ASSERT(fr.type == nfr.type); + CU_ASSERT(fr.datagram.datacnt == nfr.datagram.datacnt); + CU_ASSERT(fr.datagram.data->len == nfr.datagram.data->len); + CU_ASSERT(0 == memcmp(fr.datagram.data->base, nfr.datagram.data->base, + fr.datagram.data->len)); + + memset(&nfr, 0, sizeof(nfr)); + + /* Zero length data with length field */ + fr.type = NGTCP2_FRAME_DATAGRAM_LEN; + fr.datagram.datacnt = 0; + fr.datagram.data = NULL; + + framelen = 1 + 1; + + rv = ngtcp2_pkt_encode_datagram_frame(buf, sizeof(buf), &fr.datagram); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + + rv = ngtcp2_pkt_decode_datagram_frame(&nfr.datagram, buf, framelen); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + CU_ASSERT(fr.type == nfr.type); + CU_ASSERT(fr.datagram.datacnt == nfr.datagram.datacnt); + CU_ASSERT(NULL == nfr.datagram.data); + + /* Zero length data without length field */ + fr.type = NGTCP2_FRAME_DATAGRAM; + fr.datagram.datacnt = 0; + fr.datagram.data = NULL; + + framelen = 1; + + rv = ngtcp2_pkt_encode_datagram_frame(buf, sizeof(buf), &fr.datagram); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + + rv = ngtcp2_pkt_decode_datagram_frame(&nfr.datagram, buf, framelen); + + CU_ASSERT((ngtcp2_ssize)framelen == rv); + CU_ASSERT(fr.type == nfr.type); + CU_ASSERT(fr.datagram.datacnt == nfr.datagram.datacnt); + CU_ASSERT(NULL == nfr.datagram.data); +} + +void test_ngtcp2_pkt_adjust_pkt_num(void) { + CU_ASSERT(0xaa831f94llu == + ngtcp2_pkt_adjust_pkt_num(0xaa82f30ellu, 0x1f94, 16)); + + CU_ASSERT(0xff == ngtcp2_pkt_adjust_pkt_num(0x0100, 0xff, 8)); + CU_ASSERT(0x01ff == ngtcp2_pkt_adjust_pkt_num(0x01ff, 0xff, 8)); + CU_ASSERT(0x0fff == ngtcp2_pkt_adjust_pkt_num(0x1000, 0xff, 8)); + CU_ASSERT(0x80 == ngtcp2_pkt_adjust_pkt_num(0x00, 0x80, 8)); + CU_ASSERT(0x3fffffffffffffabllu == + ngtcp2_pkt_adjust_pkt_num(NGTCP2_MAX_PKT_NUM, 0xab, 8)); + CU_ASSERT(0x4000000000000000llu == + ngtcp2_pkt_adjust_pkt_num(NGTCP2_MAX_PKT_NUM, 0x00, 8)); + CU_ASSERT(250 == ngtcp2_pkt_adjust_pkt_num(255, 250, 8)); + CU_ASSERT(8 == ngtcp2_pkt_adjust_pkt_num(50, 8, 8)); + CU_ASSERT(0 == ngtcp2_pkt_adjust_pkt_num(-1, 0, 8)); +} + +void test_ngtcp2_pkt_validate_ack(void) { + int rv; + ngtcp2_ack fr; + + /* too long first_ack_range */ + fr.largest_ack = 1; + fr.first_ack_range = 2; + fr.rangecnt = 0; + + rv = ngtcp2_pkt_validate_ack(&fr); + + CU_ASSERT(NGTCP2_ERR_ACK_FRAME == rv); + + /* gap is too large */ + fr.largest_ack = 250; + fr.first_ack_range = 1; + fr.rangecnt = 1; + fr.ranges[0].gap = 248; + fr.ranges[0].len = 0; + + rv = ngtcp2_pkt_validate_ack(&fr); + + CU_ASSERT(NGTCP2_ERR_ACK_FRAME == rv); + + /* too large range len */ + fr.largest_ack = 250; + fr.first_ack_range = 0; + fr.rangecnt = 1; + fr.ranges[0].gap = 248; + fr.ranges[0].len = 1; + + rv = ngtcp2_pkt_validate_ack(&fr); + + CU_ASSERT(NGTCP2_ERR_ACK_FRAME == rv); +} + +void test_ngtcp2_pkt_write_stateless_reset(void) { + uint8_t buf[256]; + ngtcp2_ssize spktlen; + uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN]; + uint8_t rand[256]; + size_t i; + uint8_t *p; + size_t randlen; + + memset(rand, 0, sizeof(rand)); + for (i = 0; i < NGTCP2_STATELESS_RESET_TOKENLEN; ++i) { + token[i] = (uint8_t)(i + 1); + } + + spktlen = ngtcp2_pkt_write_stateless_reset(buf, sizeof(buf), token, rand, + sizeof(rand)); + + p = buf; + + CU_ASSERT(256 == spktlen); + CU_ASSERT(0 == (*p & NGTCP2_HEADER_FORM_BIT)); + CU_ASSERT((*p & NGTCP2_FIXED_BIT_MASK)); + + ++p; + + randlen = (size_t)(spktlen - (p - buf) - NGTCP2_STATELESS_RESET_TOKENLEN); + + CU_ASSERT(0 == memcmp(rand, p, randlen)); + + p += randlen; + + CU_ASSERT(0 == memcmp(token, p, NGTCP2_STATELESS_RESET_TOKENLEN)); + + p += NGTCP2_STATELESS_RESET_TOKENLEN; + + CU_ASSERT(spktlen == p - buf); + + /* Not enough buffer */ + spktlen = ngtcp2_pkt_write_stateless_reset( + buf, + NGTCP2_MIN_STATELESS_RESET_RANDLEN - 1 + NGTCP2_STATELESS_RESET_TOKENLEN, + token, rand, sizeof(rand)); + + CU_ASSERT(NGTCP2_ERR_NOBUF == spktlen); +} + +void test_ngtcp2_pkt_write_retry(void) { + uint8_t buf[256]; + ngtcp2_ssize spktlen; + ngtcp2_cid scid, dcid, odcid; + ngtcp2_pkt_hd nhd; + uint8_t token[32]; + size_t i; + ngtcp2_pkt_retry retry; + ngtcp2_ssize nread; + int rv; + ngtcp2_crypto_aead aead = {0}; + ngtcp2_crypto_aead_ctx aead_ctx = {0}; + uint8_t tag[NGTCP2_RETRY_TAGLEN] = {0}; + + scid_init(&scid); + dcid_init(&dcid); + rcid_init(&odcid); + + for (i = 0; i < sizeof(token); ++i) { + token[i] = (uint8_t)i; + } + + spktlen = ngtcp2_pkt_write_retry(buf, sizeof(buf), NGTCP2_PROTO_VER_V1, &dcid, + &scid, &odcid, token, sizeof(token), + null_retry_encrypt, &aead, &aead_ctx); + + CU_ASSERT(spktlen > 0); + + memset(&nhd, 0, sizeof(nhd)); + + nread = ngtcp2_pkt_decode_hd_long(&nhd, buf, (size_t)spktlen); + + CU_ASSERT(nread > 0); + CU_ASSERT(NGTCP2_PKT_RETRY == nhd.type); + CU_ASSERT(NGTCP2_PROTO_VER_V1 == nhd.version); + CU_ASSERT(ngtcp2_cid_eq(&dcid, &nhd.dcid)); + CU_ASSERT(ngtcp2_cid_eq(&scid, &nhd.scid)); + + rv = ngtcp2_pkt_decode_retry(&retry, buf + nread, (size_t)(spktlen - nread)); + + CU_ASSERT(0 == rv); + CU_ASSERT(sizeof(token) == retry.token.len); + CU_ASSERT(0 == memcmp(token, retry.token.base, sizeof(token))); + CU_ASSERT(0 == memcmp(tag, retry.tag, sizeof(tag))); +} + +void test_ngtcp2_pkt_write_version_negotiation(void) { + uint8_t buf[256]; + ngtcp2_ssize spktlen; + const uint32_t sv[] = {0xf1f2f3f4, 0x1f2f3f4f}; + const uint8_t *p; + size_t i; + ngtcp2_cid dcid, scid; + uint32_t v; + + dcid_init(&dcid); + scid_init(&scid); + + spktlen = ngtcp2_pkt_write_version_negotiation( + buf, sizeof(buf), 133, dcid.data, dcid.datalen, scid.data, scid.datalen, + sv, ngtcp2_arraylen(sv)); + + CU_ASSERT((ngtcp2_ssize)(1 + 4 + 1 + dcid.datalen + 1 + scid.datalen + + ngtcp2_arraylen(sv) * 4) == spktlen); + + p = buf; + + CU_ASSERT((0x80 | 133) == buf[0]); + + ++p; + + p = ngtcp2_get_uint32(&v, p); + + CU_ASSERT(0 == v); + + CU_ASSERT(dcid.datalen == *p); + + ++p; + + CU_ASSERT(0 == memcmp(dcid.data, p, dcid.datalen)); + + p += dcid.datalen; + + CU_ASSERT(scid.datalen == *p); + + ++p; + + CU_ASSERT(0 == memcmp(scid.data, p, scid.datalen)); + + p += scid.datalen; + + for (i = 0; i < ngtcp2_arraylen(sv); ++i) { + p = ngtcp2_get_uint32(&v, p); + + CU_ASSERT(sv[i] == v); + } +} + +void test_ngtcp2_pkt_stream_max_datalen(void) { + size_t len; + + len = ngtcp2_pkt_stream_max_datalen(63, 0, 0, 2); + + CU_ASSERT((size_t)-1 == len); + + len = ngtcp2_pkt_stream_max_datalen(63, 0, 0, 3); + + CU_ASSERT(0 == len); + + len = ngtcp2_pkt_stream_max_datalen(63, 0, 1, 3); + + CU_ASSERT(0 == len); + + len = ngtcp2_pkt_stream_max_datalen(63, 0, 1, 4); + + CU_ASSERT(1 == len); + + len = ngtcp2_pkt_stream_max_datalen(63, 1, 1, 4); + + CU_ASSERT(0 == len); + + len = ngtcp2_pkt_stream_max_datalen(63, 0, 63, 66); + + CU_ASSERT(63 == len); + + len = ngtcp2_pkt_stream_max_datalen(63, 0, 63, 65); + + CU_ASSERT(62 == len); + + len = ngtcp2_pkt_stream_max_datalen(63, 0, 1396, 1400); + + CU_ASSERT(1396 == len); + + len = ngtcp2_pkt_stream_max_datalen(63, 0, 1396, 1399); + + CU_ASSERT(1395 == len); + + len = ngtcp2_pkt_stream_max_datalen(63, 0, 1396, 9); + + CU_ASSERT(6 == len); + + len = ngtcp2_pkt_stream_max_datalen(63, 0, 16385, 16391); + + CU_ASSERT(16385 == len); + + len = ngtcp2_pkt_stream_max_datalen(63, 0, 16385, 16390); + + CU_ASSERT(16384 == len); + + len = ngtcp2_pkt_stream_max_datalen(63, 0, 1073741824, 1073741834); + + CU_ASSERT(1073741824 == len); + + len = ngtcp2_pkt_stream_max_datalen(63, 0, 1073741824, 1073741833); + + CU_ASSERT(1073741823 == len); + + len = ngtcp2_pkt_stream_max_datalen(63, 0, 16383, 16387); + + CU_ASSERT(16383 == len); +} diff --git a/tests/ngtcp2_pkt_test.h b/tests/ngtcp2_pkt_test.h new file mode 100644 index 0000000..52505eb --- /dev/null +++ b/tests/ngtcp2_pkt_test.h @@ -0,0 +1,68 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_PKT_TEST_H +#define NGTCP2_PKT_TEST_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +void test_ngtcp2_pkt_decode_version_cid(void); +void test_ngtcp2_pkt_decode_hd_long(void); +void test_ngtcp2_pkt_decode_hd_short(void); +void test_ngtcp2_pkt_decode_stream_frame(void); +void test_ngtcp2_pkt_decode_ack_frame(void); +void test_ngtcp2_pkt_decode_padding_frame(void); +void test_ngtcp2_pkt_encode_stream_frame(void); +void test_ngtcp2_pkt_encode_ack_frame(void); +void test_ngtcp2_pkt_encode_ack_ecn_frame(void); +void test_ngtcp2_pkt_encode_reset_stream_frame(void); +void test_ngtcp2_pkt_encode_connection_close_frame(void); +void test_ngtcp2_pkt_encode_connection_close_app_frame(void); +void test_ngtcp2_pkt_encode_application_close_frame(void); +void test_ngtcp2_pkt_encode_max_data_frame(void); +void test_ngtcp2_pkt_encode_max_stream_data_frame(void); +void test_ngtcp2_pkt_encode_max_streams_frame(void); +void test_ngtcp2_pkt_encode_ping_frame(void); +void test_ngtcp2_pkt_encode_data_blocked_frame(void); +void test_ngtcp2_pkt_encode_stream_data_blocked_frame(void); +void test_ngtcp2_pkt_encode_streams_blocked_frame(void); +void test_ngtcp2_pkt_encode_new_connection_id_frame(void); +void test_ngtcp2_pkt_encode_stop_sending_frame(void); +void test_ngtcp2_pkt_encode_path_challenge_frame(void); +void test_ngtcp2_pkt_encode_path_response_frame(void); +void test_ngtcp2_pkt_encode_crypto_frame(void); +void test_ngtcp2_pkt_encode_new_token_frame(void); +void test_ngtcp2_pkt_encode_retire_connection_id_frame(void); +void test_ngtcp2_pkt_encode_handshake_done_frame(void); +void test_ngtcp2_pkt_encode_datagram_frame(void); +void test_ngtcp2_pkt_adjust_pkt_num(void); +void test_ngtcp2_pkt_validate_ack(void); +void test_ngtcp2_pkt_write_stateless_reset(void); +void test_ngtcp2_pkt_write_retry(void); +void test_ngtcp2_pkt_write_version_negotiation(void); +void test_ngtcp2_pkt_stream_max_datalen(void); + +#endif /* NGTCP2_PKT_TEST_H */ diff --git a/tests/ngtcp2_pmtud_test.c b/tests/ngtcp2_pmtud_test.c new file mode 100644 index 0000000..16f4981 --- /dev/null +++ b/tests/ngtcp2_pmtud_test.c @@ -0,0 +1,153 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_pmtud_test.h" + +#include <CUnit/CUnit.h> + +#include "ngtcp2_pmtud.h" +#include "ngtcp2_test_helper.h" + +void test_ngtcp2_pmtud_probe(void) { + const ngtcp2_mem *mem = ngtcp2_mem_default(); + ngtcp2_pmtud *pmtud; + int rv; + + /* Send probe and get success */ + rv = ngtcp2_pmtud_new(&pmtud, NGTCP2_MAX_UDP_PAYLOAD_SIZE, 1452, 0, mem); + + CU_ASSERT(0 == rv); + CU_ASSERT(0 == pmtud->mtu_idx); + CU_ASSERT(!ngtcp2_pmtud_finished(pmtud)); + CU_ASSERT(ngtcp2_pmtud_require_probe(pmtud)); + CU_ASSERT(1454 - 48 == ngtcp2_pmtud_probelen(pmtud)); + + ngtcp2_pmtud_probe_sent(pmtud, 2, 0); + + CU_ASSERT(1 == pmtud->num_pkts_sent); + CU_ASSERT(2 == pmtud->expiry); + CU_ASSERT(!ngtcp2_pmtud_require_probe(pmtud)); + + ngtcp2_pmtud_handle_expiry(pmtud, 1); + + CU_ASSERT(!ngtcp2_pmtud_require_probe(pmtud)); + + ngtcp2_pmtud_handle_expiry(pmtud, 2); + + CU_ASSERT(ngtcp2_pmtud_require_probe(pmtud)); + + ngtcp2_pmtud_probe_sent(pmtud, 2, 2); + + CU_ASSERT(2 == pmtud->num_pkts_sent); + CU_ASSERT(4 == pmtud->expiry); + CU_ASSERT(!ngtcp2_pmtud_require_probe(pmtud)); + + ngtcp2_pmtud_handle_expiry(pmtud, 4); + + CU_ASSERT(ngtcp2_pmtud_require_probe(pmtud)); + + ngtcp2_pmtud_probe_sent(pmtud, 2, 4); + + CU_ASSERT(3 == pmtud->num_pkts_sent); + CU_ASSERT(10 == pmtud->expiry); + CU_ASSERT(!ngtcp2_pmtud_require_probe(pmtud)); + + ngtcp2_pmtud_probe_success(pmtud, ngtcp2_pmtud_probelen(pmtud)); + + CU_ASSERT(3 == pmtud->mtu_idx); + CU_ASSERT(UINT64_MAX == pmtud->expiry); + CU_ASSERT(0 == pmtud->num_pkts_sent); + CU_ASSERT(ngtcp2_pmtud_require_probe(pmtud)); + CU_ASSERT(1492 - 48 == ngtcp2_pmtud_probelen(pmtud)); + + ngtcp2_pmtud_probe_sent(pmtud, 2, 10); + ngtcp2_pmtud_handle_expiry(pmtud, 12); + ngtcp2_pmtud_probe_sent(pmtud, 2, 12); + ngtcp2_pmtud_handle_expiry(pmtud, 14); + ngtcp2_pmtud_probe_sent(pmtud, 2, 14); + ngtcp2_pmtud_handle_expiry(pmtud, 20); + + CU_ASSERT(1492 - 48 == pmtud->min_fail_udp_payload_size); + CU_ASSERT(ngtcp2_pmtud_finished(pmtud)); + + ngtcp2_pmtud_del(pmtud); + + /* Failing 2nd probe should skip the third probe */ + rv = ngtcp2_pmtud_new(&pmtud, NGTCP2_MAX_UDP_PAYLOAD_SIZE, 1452, 0, mem); + + ngtcp2_pmtud_probe_sent(pmtud, 2, 0); + ngtcp2_pmtud_handle_expiry(pmtud, 2); + ngtcp2_pmtud_probe_sent(pmtud, 2, 2); + ngtcp2_pmtud_handle_expiry(pmtud, 4); + ngtcp2_pmtud_probe_sent(pmtud, 2, 4); + ngtcp2_pmtud_handle_expiry(pmtud, 10); + + CU_ASSERT(1454 - 48 == pmtud->min_fail_udp_payload_size); + CU_ASSERT(1 == pmtud->mtu_idx); + + ngtcp2_pmtud_probe_sent(pmtud, 2, 10); + ngtcp2_pmtud_handle_expiry(pmtud, 12); + ngtcp2_pmtud_probe_sent(pmtud, 2, 12); + ngtcp2_pmtud_handle_expiry(pmtud, 14); + ngtcp2_pmtud_probe_sent(pmtud, 2, 14); + ngtcp2_pmtud_handle_expiry(pmtud, 20); + + CU_ASSERT(1390 - 48 == pmtud->min_fail_udp_payload_size); + CU_ASSERT(2 == pmtud->mtu_idx); + + ngtcp2_pmtud_probe_sent(pmtud, 2, 10); + ngtcp2_pmtud_probe_success(pmtud, 1280 - 48); + + CU_ASSERT(ngtcp2_pmtud_finished(pmtud)); + + ngtcp2_pmtud_del(pmtud); + + /* Skip 1st probe because it is larger than hard max. */ + rv = ngtcp2_pmtud_new(&pmtud, NGTCP2_MAX_UDP_PAYLOAD_SIZE, 1454 - 48 - 1, 0, + mem); + + CU_ASSERT(0 == rv); + CU_ASSERT(1 == pmtud->mtu_idx); + + ngtcp2_pmtud_del(pmtud); + + /* PMTUD finishes immediately because we know that all candidates + are lower than the current maximum. */ + rv = ngtcp2_pmtud_new(&pmtud, 1492 - 48, 1452, 0, mem); + + CU_ASSERT(0 == rv); + CU_ASSERT(ngtcp2_pmtud_finished(pmtud)); + + ngtcp2_pmtud_del(pmtud); + + /* PMTUD finishes immediately because the hard maximum size is lower + than the candidates. */ + rv = ngtcp2_pmtud_new(&pmtud, NGTCP2_MAX_UDP_PAYLOAD_SIZE, + NGTCP2_MAX_UDP_PAYLOAD_SIZE, 0, mem); + + CU_ASSERT(0 == rv); + CU_ASSERT(ngtcp2_pmtud_finished(pmtud)); + + ngtcp2_pmtud_del(pmtud); +} diff --git a/tests/ngtcp2_pmtud_test.h b/tests/ngtcp2_pmtud_test.h new file mode 100644 index 0000000..c4e802e --- /dev/null +++ b/tests/ngtcp2_pmtud_test.h @@ -0,0 +1,34 @@ +/* + * ngtcp2 + * + * Copyright (c) 2022 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_PMTUD_TEST_H +#define NGTCP2_PMTUD_TEST_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +void test_ngtcp2_pmtud_probe(void); + +#endif /* NGTCP2_PMTUD_TEST_H */ diff --git a/tests/ngtcp2_pv_test.c b/tests/ngtcp2_pv_test.c new file mode 100644 index 0000000..6683df1 --- /dev/null +++ b/tests/ngtcp2_pv_test.c @@ -0,0 +1,129 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_pv_test.h" + +#include <CUnit/CUnit.h> + +#include "ngtcp2_pv.h" +#include "ngtcp2_test_helper.h" + +void test_ngtcp2_pv_add_entry(void) { + ngtcp2_pv *pv; + int rv; + const ngtcp2_mem *mem = ngtcp2_mem_default(); + ngtcp2_cid cid; + const uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN] = {0xff}; + ngtcp2_dcid dcid; + ngtcp2_log log; + uint8_t data[8]; + size_t i; + ngtcp2_duration timeout = 100ULL * NGTCP2_SECONDS; + + dcid_init(&cid); + ngtcp2_dcid_init(&dcid, 1000000007, &cid, token); + ngtcp2_log_init(&log, NULL, NULL, 0, NULL); + + rv = ngtcp2_pv_new(&pv, &dcid, timeout, NGTCP2_PV_FLAG_NONE, &log, mem); + + CU_ASSERT(0 == rv); + CU_ASSERT(0 == ngtcp2_pv_validation_timed_out(pv, 0)); + + ngtcp2_pv_handle_entry_expiry(pv, 0); + + CU_ASSERT(NGTCP2_PV_NUM_PROBE_PKT == pv->probe_pkt_left); + CU_ASSERT(ngtcp2_pv_should_send_probe(pv)); + + for (i = 0; i < NGTCP2_PV_NUM_PROBE_PKT; ++i) { + ngtcp2_pv_add_entry(pv, data, 100, NGTCP2_PV_ENTRY_FLAG_NONE, 0); + + CU_ASSERT(i + 1 == ngtcp2_ringbuf_len(&pv->ents.rb)); + } + + CU_ASSERT(0 == pv->probe_pkt_left); + CU_ASSERT(!ngtcp2_pv_should_send_probe(pv)); + CU_ASSERT(NGTCP2_PV_NUM_PROBE_PKT == ngtcp2_ringbuf_len(&pv->ents.rb)); + CU_ASSERT(100 == ngtcp2_pv_next_expiry(pv)); + + ngtcp2_pv_handle_entry_expiry(pv, 99); + + CU_ASSERT(0 == pv->probe_pkt_left); + CU_ASSERT(!ngtcp2_pv_should_send_probe(pv)); + + ngtcp2_pv_handle_entry_expiry(pv, 100); + + CU_ASSERT(2 == pv->probe_pkt_left); + CU_ASSERT(ngtcp2_pv_should_send_probe(pv)); + CU_ASSERT(100 == ngtcp2_pv_next_expiry(pv)); + + ngtcp2_pv_add_entry(pv, data, 111, NGTCP2_PV_ENTRY_FLAG_NONE, 100); + + CU_ASSERT(1 == pv->probe_pkt_left); + CU_ASSERT(ngtcp2_pv_should_send_probe(pv)); + CU_ASSERT(111 == ngtcp2_pv_next_expiry(pv)); + + ngtcp2_pv_del(pv); +} + +void test_ngtcp2_pv_validate(void) { + ngtcp2_pv *pv; + int rv; + const ngtcp2_mem *mem = ngtcp2_mem_default(); + ngtcp2_cid cid; + const uint8_t token[NGTCP2_STATELESS_RESET_TOKENLEN] = {0xff}; + ngtcp2_dcid dcid; + ngtcp2_log log; + uint8_t data[8]; + ngtcp2_duration timeout = 100ULL * NGTCP2_SECONDS; + ngtcp2_path_storage path; + uint8_t flags; + + path_init(&path, 1, 0, 2, 0); + dcid_init(&cid); + ngtcp2_dcid_init(&dcid, 1000000007, &cid, token); + ngtcp2_path_copy(&dcid.ps.path, &path.path); + ngtcp2_log_init(&log, NULL, NULL, 0, NULL); + + rv = ngtcp2_pv_new(&pv, &dcid, timeout, NGTCP2_PV_FLAG_NONE, &log, mem); + + CU_ASSERT(0 == rv); + + memset(data, 0, sizeof(data)); + ngtcp2_pv_add_entry(pv, data, 100, NGTCP2_PV_ENTRY_FLAG_NONE, 1); + + memset(data, 1, sizeof(data)); + ngtcp2_pv_add_entry(pv, data, 100, NGTCP2_PV_ENTRY_FLAG_NONE, 1); + + memset(data, 1, sizeof(data)); + rv = ngtcp2_pv_validate(pv, &flags, data); + + CU_ASSERT(0 == rv); + + memset(data, 3, sizeof(data)); + rv = ngtcp2_pv_validate(pv, &flags, data); + + CU_ASSERT(NGTCP2_ERR_INVALID_ARGUMENT == rv); + + ngtcp2_pv_del(pv); +} diff --git a/tests/ngtcp2_pv_test.h b/tests/ngtcp2_pv_test.h new file mode 100644 index 0000000..331dc09 --- /dev/null +++ b/tests/ngtcp2_pv_test.h @@ -0,0 +1,35 @@ +/* + * ngtcp2 + * + * Copyright (c) 2019 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_PV_TEST_H +#define NGTCP2_PV_TEST_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +void test_ngtcp2_pv_add_entry(void); +void test_ngtcp2_pv_validate(void); + +#endif /* NGTCP2_PV_TEST_H */ diff --git a/tests/ngtcp2_range_test.c b/tests/ngtcp2_range_test.c new file mode 100644 index 0000000..d60c343 --- /dev/null +++ b/tests/ngtcp2_range_test.c @@ -0,0 +1,105 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_range_test.h" + +#include <CUnit/CUnit.h> + +#include "ngtcp2_range.h" +#include "ngtcp2_test_helper.h" + +void test_ngtcp2_range_intersect(void) { + ngtcp2_range a, b, c; + + ngtcp2_range_init(&a, 0, UINT64_MAX); + ngtcp2_range_init(&b, 0, 1000000007); + c = ngtcp2_range_intersect(&a, &b); + + CU_ASSERT(0 == c.begin); + CU_ASSERT(1000000007 == c.end); + + ngtcp2_range_init(&a, 1000000007, UINT64_MAX); + ngtcp2_range_init(&b, 0, UINT64_MAX); + c = ngtcp2_range_intersect(&a, &b); + + CU_ASSERT(1000000007 == c.begin); + CU_ASSERT(UINT64_MAX == c.end); + + ngtcp2_range_init(&a, 0, UINT64_MAX); + ngtcp2_range_init(&b, 33333, 55555); + c = ngtcp2_range_intersect(&a, &b); + + CU_ASSERT(33333 == c.begin); + CU_ASSERT(55555 == c.end); + + ngtcp2_range_init(&a, 0, 1000000009); + ngtcp2_range_init(&b, 1000000007, UINT64_MAX); + c = ngtcp2_range_intersect(&a, &b); + + CU_ASSERT(1000000007 == c.begin); + CU_ASSERT(1000000009 == c.end); +} + +void test_ngtcp2_range_cut(void) { + ngtcp2_range a, b, l, r; + + ngtcp2_range_init(&a, 0, UINT64_MAX); + ngtcp2_range_init(&b, 1000000007, 1000000009); + ngtcp2_range_cut(&l, &r, &a, &b); + + CU_ASSERT(0 == l.begin); + CU_ASSERT(1000000007 == l.end); + CU_ASSERT(1000000009 == r.begin); + CU_ASSERT(UINT64_MAX == r.end); + + ngtcp2_range_init(&a, 0, UINT64_MAX); + ngtcp2_range_init(&b, 0, 1000000007); + ngtcp2_range_cut(&l, &r, &a, &b); + + CU_ASSERT(0 == ngtcp2_range_len(&l)); + CU_ASSERT(1000000007 == r.begin); + CU_ASSERT(UINT64_MAX == r.end); + + ngtcp2_range_init(&a, 0, UINT64_MAX); + ngtcp2_range_init(&b, 1000000009, UINT64_MAX); + ngtcp2_range_cut(&l, &r, &a, &b); + + CU_ASSERT(0 == l.begin); + CU_ASSERT(1000000009 == l.end); + CU_ASSERT(0 == ngtcp2_range_len(&r)); +} + +void test_ngtcp2_range_not_after(void) { + ngtcp2_range a, b; + + ngtcp2_range_init(&a, 1, 1000000007); + ngtcp2_range_init(&b, 0, 1000000007); + + CU_ASSERT(ngtcp2_range_not_after(&a, &b)); + + ngtcp2_range_init(&a, 1, 1000000008); + ngtcp2_range_init(&b, 0, 1000000007); + + CU_ASSERT(!ngtcp2_range_not_after(&a, &b)); +} diff --git a/tests/ngtcp2_range_test.h b/tests/ngtcp2_range_test.h new file mode 100644 index 0000000..a8d2d9c --- /dev/null +++ b/tests/ngtcp2_range_test.h @@ -0,0 +1,36 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_RANGE_TEST_H +#define NGTCP2_RANGE_TEST_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +void test_ngtcp2_range_intersect(void); +void test_ngtcp2_range_cut(void); +void test_ngtcp2_range_not_after(void); + +#endif /* NGTCP2_RANGE_TEST_H */ diff --git a/tests/ngtcp2_ringbuf_test.c b/tests/ngtcp2_ringbuf_test.c new file mode 100644 index 0000000..b6440f5 --- /dev/null +++ b/tests/ngtcp2_ringbuf_test.c @@ -0,0 +1,91 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_ringbuf_test.h" + +#include <CUnit/CUnit.h> + +#include "ngtcp2_ringbuf.h" +#include "ngtcp2_test_helper.h" + +typedef struct { + int32_t a; + uint64_t b; +} ints; + +void test_ngtcp2_ringbuf_push_front(void) { + ngtcp2_ringbuf rb; + const ngtcp2_mem *mem = ngtcp2_mem_default(); + size_t i; + + ngtcp2_ringbuf_init(&rb, 64, sizeof(ints), mem); + + for (i = 0; i < 64; ++i) { + ints *p = ngtcp2_ringbuf_push_front(&rb); + p->a = (int32_t)(i + 1); + p->b = (i + 1) * 10; + } + + CU_ASSERT(64 == ngtcp2_ringbuf_len(&rb)); + + for (i = 0; i < 64; ++i) { + ints *p = ngtcp2_ringbuf_get(&rb, i); + CU_ASSERT((int32_t)(64 - i) == p->a); + CU_ASSERT((64 - i) * 10 == p->b); + } + + ngtcp2_ringbuf_push_front(&rb); + + CU_ASSERT(64 == ngtcp2_ringbuf_len(&rb)); + CU_ASSERT((int32_t)64 == ((ints *)ngtcp2_ringbuf_get(&rb, 1))->a); + + ngtcp2_ringbuf_free(&rb); +} + +void test_ngtcp2_ringbuf_pop_front(void) { + ngtcp2_ringbuf rb; + const ngtcp2_mem *mem = ngtcp2_mem_default(); + size_t i; + + ngtcp2_ringbuf_init(&rb, 4, sizeof(ints), mem); + + for (i = 0; i < 5; ++i) { + ints *p = ngtcp2_ringbuf_push_front(&rb); + p->a = (int32_t)i; + } + + CU_ASSERT(4 == ngtcp2_ringbuf_len(&rb)); + + for (i = 4; i >= 1; --i) { + ints *p = ngtcp2_ringbuf_get(&rb, 0); + + CU_ASSERT((int32_t)i == p->a); + + ngtcp2_ringbuf_pop_front(&rb); + } + + CU_ASSERT(0 == ngtcp2_ringbuf_len(&rb)); + + ngtcp2_ringbuf_free(&rb); +} diff --git a/tests/ngtcp2_ringbuf_test.h b/tests/ngtcp2_ringbuf_test.h new file mode 100644 index 0000000..7ad8b44 --- /dev/null +++ b/tests/ngtcp2_ringbuf_test.h @@ -0,0 +1,35 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_RINGBUF_TEST_H +#define NGTCP2_RINGBUF_TEST_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +void test_ngtcp2_ringbuf_push_front(void); +void test_ngtcp2_ringbuf_pop_front(void); + +#endif /* NGTCP2_RINGBUF_TEST_H */ diff --git a/tests/ngtcp2_rob_test.c b/tests/ngtcp2_rob_test.c new file mode 100644 index 0000000..93a2641 --- /dev/null +++ b/tests/ngtcp2_rob_test.c @@ -0,0 +1,552 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_rob_test.h" + +#include <CUnit/CUnit.h> + +#include "ngtcp2_rob.h" +#include "ngtcp2_test_helper.h" +#include "ngtcp2_mem.h" + +void test_ngtcp2_rob_push(void) { + const ngtcp2_mem *mem = ngtcp2_mem_default(); + ngtcp2_rob rob; + int rv; + uint8_t data[256]; + ngtcp2_rob_gap *g; + ngtcp2_ksl_it it; + + /* Check range overlapping */ + ngtcp2_rob_init(&rob, 64, mem); + + rv = ngtcp2_rob_push(&rob, 34567, data, 145); + + CU_ASSERT(0 == rv); + + it = ngtcp2_ksl_begin(&rob.gapksl); + g = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(0 == g->range.begin); + CU_ASSERT(34567 == g->range.end); + + ngtcp2_ksl_it_next(&it); + g = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(34567 + 145 == g->range.begin); + CU_ASSERT(UINT64_MAX == g->range.end); + + ngtcp2_ksl_it_next(&it); + + CU_ASSERT(ngtcp2_ksl_it_end(&it)); + + rv = ngtcp2_rob_push(&rob, 34565, data, 1); + + CU_ASSERT(0 == rv); + + it = ngtcp2_ksl_begin(&rob.gapksl); + g = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(0 == g->range.begin); + CU_ASSERT(34565 == g->range.end); + + ngtcp2_ksl_it_next(&it); + g = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(34566 == g->range.begin); + CU_ASSERT(34567 == g->range.end); + + rv = ngtcp2_rob_push(&rob, 34563, data, 1); + + CU_ASSERT(0 == rv); + + it = ngtcp2_ksl_begin(&rob.gapksl); + g = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(0 == g->range.begin); + CU_ASSERT(34563 == g->range.end); + + ngtcp2_ksl_it_next(&it); + g = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(34564 == g->range.begin); + CU_ASSERT(34565 == g->range.end); + + rv = ngtcp2_rob_push(&rob, 34561, data, 151); + + CU_ASSERT(0 == rv); + + it = ngtcp2_ksl_begin(&rob.gapksl); + g = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(0 == g->range.begin); + CU_ASSERT(34561 == g->range.end); + + ngtcp2_ksl_it_next(&it); + g = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(34567 + 145 == g->range.begin); + CU_ASSERT(UINT64_MAX == g->range.end); + + ngtcp2_ksl_it_next(&it); + + CU_ASSERT(ngtcp2_ksl_it_end(&it)); + + ngtcp2_rob_free(&rob); + + /* Check removing prefix */ + ngtcp2_rob_init(&rob, 64, mem); + + rv = ngtcp2_rob_push(&rob, 0, data, 123); + + CU_ASSERT(0 == rv); + + it = ngtcp2_ksl_begin(&rob.gapksl); + g = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(123 == g->range.begin); + CU_ASSERT(UINT64_MAX == g->range.end); + + ngtcp2_ksl_it_next(&it); + + CU_ASSERT(ngtcp2_ksl_it_end(&it)); + + ngtcp2_rob_free(&rob); + + /* Check removing suffix */ + ngtcp2_rob_init(&rob, 64, mem); + + rv = ngtcp2_rob_push(&rob, UINT64_MAX - 123, data, 123); + + CU_ASSERT(0 == rv); + + it = ngtcp2_ksl_begin(&rob.gapksl); + g = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(0 == g->range.begin); + CU_ASSERT(UINT64_MAX - 123 == g->range.end); + + ngtcp2_ksl_it_next(&it); + + CU_ASSERT(ngtcp2_ksl_it_end(&it)); + + ngtcp2_rob_free(&rob); +} + +static ngtcp2_range randkeys[] = { + {25996, 26260}, {9431, 9555}, {9113, 9417}, {2992, 3408}, + {35761, 36019}, {38891, 39113}, {30074, 30325}, {9525, 9953}, + {31708, 31944}, {24554, 24864}, {13097, 13472}, {47253, 47400}, + {18424, 18742}, {4618, 4889}, {40871, 41076}, {17642, 18068}, + {47496, 47588}, {1226, 1283}, {17904, 18248}, {9221, 9488}, + {8621, 8773}, {27912, 28344}, {5878, 6121}, {37336, 37545}, + {15403, 15557}, {29314, 29450}, {2342, 2595}, {34000, 34356}, + {46428, 46828}, {40624, 40703}, {47014, 47319}, {13353, 13635}, + {14466, 14682}, {22446, 22654}, {10035, 10140}, {1005, 1410}, + {3741, 4133}, {45734, 46053}, {7954, 8214}, {32666, 32796}, + {45236, 45531}, {32100, 32501}, {25466, 25850}, {2845, 3179}, + {23525, 23991}, {46367, 46459}, {37712, 38164}, {8506, 8680}, + {31702, 31752}, {33364, 33825}, {14284, 14614}, {22928, 23344}, + {29058, 29155}, {36639, 37014}, {29133, 29445}, {31071, 31478}, + {40074, 40370}, {1263, 1383}, {7908, 8181}, {40426, 40716}, + {4830, 5053}, {38241, 38645}, {51197, 51401}, {36180, 36301}, + {14920, 15262}, {5707, 5882}, {32697, 32948}, {42324, 42791}, + {1543, 1732}, {11037, 11395}, {36534, 36707}, {26093, 26322}, + {41862, 42213}, {1373, 1745}, {31322, 31706}, {45474, 45851}, + {19333, 19701}, {49172, 49524}, {10641, 10932}, {17459, 17630}, + {5560, 5936}, {7657, 7988}, {3300, 3357}, {2496, 2600}, + {46018, 46173}, {43127, 43239}, {48949, 49036}, {45094, 45412}, + {8405, 8738}, {8687, 9168}, {41405, 41759}, {22014, 22474}, + {16097, 16426}, {29611, 29931}, {46054, 46250}, {26305, 26545}, + {13696, 13964}, {26899, 26981}, {30797, 30936}, {34125, 34235}, + {50016, 50058}, {46775, 47005}, {4891, 5106}, {12720, 12994}, + {44623, 44967}, {33597, 34060}, {50796, 51295}, {18862, 19242}, + {36166, 36249}, {22237, 22583}, {18188, 18586}, {21376, 21447}, + {49563, 49800}, {10121, 10272}, {39156, 39275}, {17609, 17866}, + {47609, 47829}, {34311, 34631}, {2144, 2433}, {34692, 34824}, + {8309, 8476}, {26969, 27447}, {40651, 40952}, {11906, 12116}, + {22467, 22864}, {35535, 35941}, {33061, 33259}, {21006, 21364}, + {15212, 15504}, {6954, 7356}, {6126, 6405}, {29268, 29514}, + {35221, 35505}, {4163, 4350}, {17374, 17519}, {16170, 16511}, + {37142, 37440}, {6288, 6556}, {27795, 28092}, {35381, 35476}, + {1186, 1455}, {39834, 40197}, {3471, 3906}, {46871, 47242}, + {40258, 40406}, {0, 306}, {31852, 32133}, {23314, 23408}, + {37494, 37625}, {48742, 48990}, {37616, 37905}, {18615, 18991}, + {2561, 2921}, {47767, 48139}, {39616, 39792}, {44791, 45046}, + {2770, 3067}, {16697, 17083}, {9216, 9427}, {37661, 37774}, + {14666, 14976}, {31547, 31819}, {36052, 36356}, {34989, 35285}, + {1651, 2028}, {36264, 36515}, {10257, 10551}, {24381, 24628}, + {28428, 28726}, {4242, 4576}, {44972, 45107}, {12970, 13213}, + {19539, 19828}, {42541, 42763}, {20349, 20630}, {20138, 20418}, + {10884, 11138}, {2717, 2908}, {8292, 8399}, {712, 1101}, + {44451, 44741}, {28660, 28946}, {40955, 41253}, {29424, 29864}, + {14177, 14446}, {30219, 30632}, {24757, 25012}, {47991, 48306}, + {42054, 42252}, {3984, 4419}, {42304, 42506}, {7160, 7543}, + {2004, 2152}, {9777, 10105}, {15724, 16008}, {11263, 11573}, + {15066, 15239}, {12108, 12336}, {17138, 17570}, {30472, 30714}, + {41197, 41294}, {24294, 24496}, {17371, 17514}, {11426, 11749}, + {25223, 25474}, {18083, 18345}, {27611, 27919}, {8116, 8261}, + {40317, 40373}, {46652, 47026}, {18082, 18151}, {19808, 19970}, + {46627, 46885}, {11646, 11789}, {1498, 1687}, {35907, 36081}, + {36340, 36593}, {1255, 1311}, {43485, 43551}, {6586, 6895}, + {10331, 10467}, {26803, 26998}, {14007, 14360}, {35951, 36120}, + {37327, 37592}, {35419, 35724}, {50379, 50514}, {37251, 37489}, + {27313, 27752}, {27502, 27845}, {36608, 36732}, {41751, 42057}, + {19118, 19267}, {16529, 16926}, {49794, 50066}, {37378, 37699}, + {7440, 7552}, {10418, 10650}, {50184, 50635}, {44350, 44579}, + {8178, 8502}, {33838, 34017}, {11582, 11864}, {11756, 11785}, + {42136, 42328}, {39404, 39545}, {13924, 14209}, {29411, 29627}, + {10836, 11139}, {40332, 40598}, {26097, 26561}, {5422, 5512}, + {30687, 30849}, {4399, 4726}, {50679, 50762}, {41224, 41439}, + {46023, 46129}, {22690, 23010}, {37920, 38085}, {25885, 26249}, + {51047, 51185}, {21508, 21904}, {6731, 7010}, {38144, 38493}, + {47648, 47886}, {120, 603}, {49964, 50182}, {43503, 43765}, + {24092, 24436}, {19204, 19509}, {19668, 19930}, {6815, 6963}, + {10552, 10775}, {949, 1239}, {36976, 37348}, {34806, 34901}, + {19939, 20308}, {42245, 42329}, {42700, 43067}, {13821, 14054}, + {28109, 28331}, {32929, 33212}, {23736, 24036}, {31969, 32240}, + {12326, 12612}, {5999, 6132}, {42871, 43283}, {33204, 33496}, + {5757, 5991}, {46826, 46927}, {4994, 5278}, {47371, 47713}, + {20886, 21106}, {38457, 38794}, {48451, 48789}, {34146, 34343}, + {45911, 46248}, {48215, 48615}, {43970, 44131}, {30886, 31216}, + {50135, 50292}, {3726, 3854}, {39041, 39408}, {48617, 48756}, + {46205, 46590}, {39766, 39923}, {20835, 21106}, {43716, 44066}, + {45665, 45789}, {12549, 12755}, {23366, 23752}, {17864, 17942}, + {28288, 28528}, {2744, 2941}, {49355, 49605}, {34527, 34816}, + {23092, 23447}, {5832, 5912}, {21146, 21478}, {30784, 30884}, + {28221, 28469}, {34944, 35047}, {23956, 24126}, {7538, 7890}, + {32496, 32803}, {16404, 16607}, {37968, 38277}, {7399, 7574}, + {28605, 28842}, {50454, 50851}, {20581, 20845}, {21395, 21705}, + {50726, 50871}, {11953, 12278}, {533, 822}, {5298, 5658}, + {48707, 48914}, {21760, 22223}, {1889, 2146}, {6409, 6842}, + {44094, 44473}, {18003, 18336}, {41550, 41926}, {50042, 50136}, + {38646, 38835}, {5425, 5693}, {48967, 49383}, {376, 596}, + {47514, 47704}, {43238, 43663}, {25440, 25655}, {25652, 26050}, + {16909, 17232}, {41312, 41490}, {5909, 6049}, {3153, 3523}, + {27877, 28046}, {26715, 26810}, {10031, 10108}, {32282, 32620}, + {8934, 9219}, {5133, 5493}, {26666, 26787}, {45324, 45630}, + {34880, 35008}, {20823, 20920}, {39571, 39704}, {15523, 15869}, + {4360, 4637}, {46199, 46384}, {35991, 36242}, {46852, 46931}, + {39218, 39644}, {11785, 12029}, {27225, 27366}, {29820, 30097}, + {36778, 37072}, {9871, 10255}, {51065, 51208}, {38775, 39102}, + {39446, 39712}, {33856, 34083}, {28853, 29289}, {526, 666}, + {37510, 37697}, {13455, 13855}, {25648, 25691}, {10694, 11041}, + {26441, 26889}, {18821, 19058}, {3357, 3590}, {15915, 16276}, + {37706, 37934}, {24970, 25281}, {43951, 44124}, {35874, 36128}, +}; + +void test_ngtcp2_rob_push_random(void) { + const ngtcp2_mem *mem = ngtcp2_mem_default(); + ngtcp2_rob rob; + int rv; + uint8_t data[512]; + size_t i; + + ngtcp2_rob_init(&rob, 1024 * 1024, mem); + for (i = 0; i < ngtcp2_arraylen(randkeys); ++i) { + rv = ngtcp2_rob_push(&rob, randkeys[i].begin, &data[0], + (size_t)ngtcp2_range_len(&randkeys[i])); + + CU_ASSERT(0 == rv); + } + + CU_ASSERT(51401 == ngtcp2_rob_first_gap_offset(&rob)); + + ngtcp2_rob_free(&rob); +} + +void test_ngtcp2_rob_data_at(void) { + const ngtcp2_mem *mem = ngtcp2_mem_default(); + ngtcp2_rob rob; + int rv; + uint8_t data[256]; + size_t i; + const uint8_t *p; + size_t len; + ngtcp2_rob_data *d; + ngtcp2_ksl_it it; + ngtcp2_rob_gap *g; + + for (i = 0; i < sizeof(data); ++i) { + data[i] = (uint8_t)i; + } + + ngtcp2_rob_init(&rob, 16, mem); + + rv = ngtcp2_rob_push(&rob, 3, &data[3], 13); + + CU_ASSERT(0 == rv); + + len = ngtcp2_rob_data_at(&rob, &p, 0); + + CU_ASSERT(0 == len); + + rv = ngtcp2_rob_push(&rob, 0, &data[0], 3); + + CU_ASSERT(0 == rv); + + len = ngtcp2_rob_data_at(&rob, &p, 0); + + CU_ASSERT(16 == len); + + for (i = 0; i < len; ++i) { + CU_ASSERT((uint8_t)i == *(p + i)); + } + + ngtcp2_rob_pop(&rob, 0, len); + + rv = ngtcp2_rob_push(&rob, 16, &data[16], 5); + + CU_ASSERT(0 == rv); + + len = ngtcp2_rob_data_at(&rob, &p, 16); + + CU_ASSERT(5 == len); + + for (i = 16; i < len; ++i) { + CU_ASSERT((uint8_t)i == *(p + i)); + } + + ngtcp2_rob_free(&rob); + + /* Verify the case where data spans over multiple chunks */ + ngtcp2_rob_init(&rob, 16, mem); + + rv = ngtcp2_rob_push(&rob, 0, &data[0], 47); + + CU_ASSERT(0 == rv); + + len = ngtcp2_rob_data_at(&rob, &p, 0); + + CU_ASSERT(16 == len); + + ngtcp2_rob_pop(&rob, 0, len); + len = ngtcp2_rob_data_at(&rob, &p, 16); + + CU_ASSERT(16 == len); + + ngtcp2_rob_pop(&rob, 16, len); + len = ngtcp2_rob_data_at(&rob, &p, 32); + + CU_ASSERT(15 == len); + + ngtcp2_rob_pop(&rob, 32, len); + ngtcp2_rob_free(&rob); + + /* Verify the case where new offset comes before the existing + chunk */ + ngtcp2_rob_init(&rob, 16, mem); + + rv = ngtcp2_rob_push(&rob, 17, &data[17], 2); + + CU_ASSERT(0 == rv); + + len = ngtcp2_rob_data_at(&rob, &p, 0); + + CU_ASSERT(0 == len); + + rv = ngtcp2_rob_push(&rob, 0, &data[0], 3); + + CU_ASSERT(0 == rv); + + len = ngtcp2_rob_data_at(&rob, &p, 0); + + CU_ASSERT(3 == len); + + ngtcp2_rob_pop(&rob, 0, len); + + len = ngtcp2_rob_data_at(&rob, &p, 3); + + CU_ASSERT(0 == len); + + ngtcp2_rob_free(&rob); + + /* Verify the case where new offset comes after the existing + chunk */ + ngtcp2_rob_init(&rob, 16, mem); + + rv = ngtcp2_rob_push(&rob, 0, &data[0], 3); + + CU_ASSERT(0 == rv); + + rv = ngtcp2_rob_push(&rob, 16, &data[16], 32); + + CU_ASSERT(0 == rv); + + it = ngtcp2_ksl_begin(&rob.dataksl); + ngtcp2_ksl_it_next(&it); + d = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(16 == d->range.begin); + + ngtcp2_ksl_it_next(&it); + d = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(32 == d->range.begin); + + ngtcp2_ksl_it_next(&it); + + CU_ASSERT(ngtcp2_ksl_it_end(&it)); + + ngtcp2_rob_free(&rob); + + /* Severely scattered data */ + ngtcp2_rob_init(&rob, 16, mem); + + for (i = 0; i < sizeof(data); i += 2) { + rv = ngtcp2_rob_push(&rob, i, &data[i], 1); + + CU_ASSERT(0 == rv); + } + + for (i = 1; i < sizeof(data); i += 2) { + rv = ngtcp2_rob_push(&rob, i, &data[i], 1); + + CU_ASSERT(0 == rv); + } + + for (i = 0; i < sizeof(data) / 16; ++i) { + len = ngtcp2_rob_data_at(&rob, &p, i * 16); + + CU_ASSERT(16 == len); + + ngtcp2_rob_pop(&rob, i * 16, len); + } + + it = ngtcp2_ksl_begin(&rob.gapksl); + g = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(256 == g->range.begin); + + it = ngtcp2_ksl_begin(&rob.dataksl); + + CU_ASSERT(ngtcp2_ksl_it_end(&it)); + + ngtcp2_rob_free(&rob); + + /* Verify the case where chunk is reused if it is not fully used */ + ngtcp2_rob_init(&rob, 16, mem); + + rv = ngtcp2_rob_push(&rob, 0, &data[0], 5); + + CU_ASSERT(0 == rv); + + len = ngtcp2_rob_data_at(&rob, &p, 0); + + CU_ASSERT(5 == len); + + ngtcp2_rob_pop(&rob, 0, len); + + rv = ngtcp2_rob_push(&rob, 2, &data[2], 8); + + CU_ASSERT(0 == rv); + + len = ngtcp2_rob_data_at(&rob, &p, 5); + + CU_ASSERT(5 == len); + + ngtcp2_rob_pop(&rob, 5, len); + + ngtcp2_rob_free(&rob); + + /* Verify the case where 2nd push covers already processed region */ + ngtcp2_rob_init(&rob, 16, mem); + + rv = ngtcp2_rob_push(&rob, 0, &data[0], 16); + + CU_ASSERT(0 == rv); + + len = ngtcp2_rob_data_at(&rob, &p, 0); + + CU_ASSERT(16 == len); + + ngtcp2_rob_pop(&rob, 0, len); + + rv = ngtcp2_rob_push(&rob, 0, &data[0], 32); + + CU_ASSERT(0 == rv); + + len = ngtcp2_rob_data_at(&rob, &p, 16); + + CU_ASSERT(16 == len); + + ngtcp2_rob_pop(&rob, 16, len); + + ngtcp2_rob_free(&rob); +} + +void test_ngtcp2_rob_remove_prefix(void) { + const ngtcp2_mem *mem = ngtcp2_mem_default(); + ngtcp2_rob rob; + ngtcp2_rob_gap *g; + ngtcp2_rob_data *d; + ngtcp2_ksl_it it; + uint8_t data[256]; + int rv; + + /* Removing data which spans multiple chunks */ + ngtcp2_rob_init(&rob, 16, mem); + + rv = ngtcp2_rob_push(&rob, 1, &data[1], 32); + + CU_ASSERT(0 == rv); + + ngtcp2_rob_remove_prefix(&rob, 33); + + it = ngtcp2_ksl_begin(&rob.gapksl); + g = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(33 == g->range.begin); + + it = ngtcp2_ksl_begin(&rob.dataksl); + d = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(32 == d->range.begin); + + ngtcp2_rob_free(&rob); + + /* Remove an entire gap */ + ngtcp2_rob_init(&rob, 16, mem); + + rv = ngtcp2_rob_push(&rob, 1, &data[1], 3); + + CU_ASSERT(0 == rv); + + rv = ngtcp2_rob_push(&rob, 5, &data[5], 2); + + CU_ASSERT(0 == rv); + + ngtcp2_rob_remove_prefix(&rob, 16); + + it = ngtcp2_ksl_begin(&rob.gapksl); + g = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(16 == g->range.begin); + + ngtcp2_ksl_it_next(&it); + + CU_ASSERT(ngtcp2_ksl_it_end(&it)); + + ngtcp2_rob_free(&rob); +} diff --git a/tests/ngtcp2_rob_test.h b/tests/ngtcp2_rob_test.h new file mode 100644 index 0000000..e1f54b6 --- /dev/null +++ b/tests/ngtcp2_rob_test.h @@ -0,0 +1,37 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_ROB_TEST_H +#define NGTCP2_ROB_TEST_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +void test_ngtcp2_rob_push(void); +void test_ngtcp2_rob_push_random(void); +void test_ngtcp2_rob_data_at(void); +void test_ngtcp2_rob_remove_prefix(void); + +#endif /* NGTCP2_ROB_TEST_H */ diff --git a/tests/ngtcp2_rtb_test.c b/tests/ngtcp2_rtb_test.c new file mode 100644 index 0000000..7c91c38 --- /dev/null +++ b/tests/ngtcp2_rtb_test.c @@ -0,0 +1,470 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_rtb_test.h" + +#include <assert.h> + +#include <CUnit/CUnit.h> + +#include "ngtcp2_rtb.h" +#include "ngtcp2_test_helper.h" +#include "ngtcp2_mem.h" +#include "ngtcp2_pkt.h" + +static void conn_stat_init(ngtcp2_conn_stat *cstat) { + memset(cstat, 0, sizeof(*cstat)); + cstat->max_tx_udp_payload_size = NGTCP2_MAX_UDP_PAYLOAD_SIZE; +} + +void test_ngtcp2_rtb_add(void) { + ngtcp2_rtb rtb; + ngtcp2_rtb_entry *ent; + int rv; + const ngtcp2_mem *mem = ngtcp2_mem_default(); + ngtcp2_pkt_hd hd; + ngtcp2_log log; + ngtcp2_cid dcid; + ngtcp2_ksl_it it; + ngtcp2_conn_stat cstat; + ngtcp2_cc cc; + ngtcp2_strm crypto; + const ngtcp2_pktns_id pktns_id = NGTCP2_PKTNS_ID_HANDSHAKE; + ngtcp2_rst rst; + ngtcp2_objalloc frc_objalloc; + ngtcp2_objalloc rtb_entry_objalloc; + + ngtcp2_objalloc_init(&frc_objalloc, 1024, mem); + ngtcp2_objalloc_init(&rtb_entry_objalloc, 1024, mem); + + ngtcp2_strm_init(&crypto, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, NULL, &frc_objalloc, + mem); + dcid_init(&dcid); + conn_stat_init(&cstat); + ngtcp2_rst_init(&rst); + ngtcp2_log_init(&log, NULL, NULL, 0, NULL); + ngtcp2_cc_reno_cc_init(&cc, &log, mem); + ngtcp2_rtb_init(&rtb, pktns_id, &crypto, &rst, &cc, &log, NULL, + &rtb_entry_objalloc, &frc_objalloc, mem); + + ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_1RTT, &dcid, NULL, + 1000000007, 1, NGTCP2_PROTO_VER_V1, 0); + + rv = ngtcp2_rtb_entry_objalloc_new( + &ent, &hd, NULL, 10, 0, NGTCP2_RTB_ENTRY_FLAG_NONE, &rtb_entry_objalloc); + + CU_ASSERT(0 == rv); + + ngtcp2_rtb_add(&rtb, ent, &cstat); + + ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_1RTT, &dcid, NULL, + 1000000008, 2, NGTCP2_PROTO_VER_V1, 0); + + rv = ngtcp2_rtb_entry_objalloc_new( + &ent, &hd, NULL, 9, 0, NGTCP2_RTB_ENTRY_FLAG_NONE, &rtb_entry_objalloc); + + CU_ASSERT(0 == rv); + + ngtcp2_rtb_add(&rtb, ent, &cstat); + + ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_1RTT, &dcid, NULL, + 1000000009, 4, NGTCP2_PROTO_VER_V1, 0); + + rv = ngtcp2_rtb_entry_objalloc_new( + &ent, &hd, NULL, 11, 0, NGTCP2_RTB_ENTRY_FLAG_NONE, &rtb_entry_objalloc); + + CU_ASSERT(0 == rv); + + ngtcp2_rtb_add(&rtb, ent, &cstat); + + it = ngtcp2_rtb_head(&rtb); + ent = ngtcp2_ksl_it_get(&it); + + /* Check the top of the queue */ + CU_ASSERT(1000000009 == ent->hd.pkt_num); + + ngtcp2_ksl_it_next(&it); + ent = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(1000000008 == ent->hd.pkt_num); + + ngtcp2_ksl_it_next(&it); + ent = ngtcp2_ksl_it_get(&it); + + CU_ASSERT(1000000007 == ent->hd.pkt_num); + + ngtcp2_ksl_it_next(&it); + + CU_ASSERT(ngtcp2_ksl_it_end(&it)); + + ngtcp2_rtb_free(&rtb); + ngtcp2_cc_reno_cc_free(&cc, mem); + ngtcp2_strm_free(&crypto); + + ngtcp2_objalloc_free(&rtb_entry_objalloc); + ngtcp2_objalloc_free(&frc_objalloc); +} + +static void add_rtb_entry_range(ngtcp2_rtb *rtb, int64_t base_pkt_num, + size_t len, ngtcp2_conn_stat *cstat, + ngtcp2_objalloc *objalloc) { + ngtcp2_pkt_hd hd; + ngtcp2_rtb_entry *ent; + size_t i; + ngtcp2_cid dcid; + + dcid_init(&dcid); + + for (i = 0; i < len; ++i) { + ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_1RTT, &dcid, NULL, + base_pkt_num + (int64_t)i, 1, NGTCP2_PROTO_VER_V1, 0); + ngtcp2_rtb_entry_objalloc_new(&ent, &hd, NULL, 0, 0, + NGTCP2_RTB_ENTRY_FLAG_NONE, objalloc); + ngtcp2_rtb_add(rtb, ent, cstat); + } +} + +static void setup_rtb_fixture(ngtcp2_rtb *rtb, ngtcp2_conn_stat *cstat, + ngtcp2_objalloc *objalloc) { + /* 100, ..., 154 */ + add_rtb_entry_range(rtb, 100, 55, cstat, objalloc); + /* 180, ..., 184 */ + add_rtb_entry_range(rtb, 180, 5, cstat, objalloc); + /* 440, ..., 446 */ + add_rtb_entry_range(rtb, 440, 7, cstat, objalloc); +} + +static void assert_rtb_entry_not_found(ngtcp2_rtb *rtb, int64_t pkt_num) { + ngtcp2_ksl_it it = ngtcp2_rtb_head(rtb); + ngtcp2_rtb_entry *ent; + + for (; !ngtcp2_ksl_it_end(&it); ngtcp2_ksl_it_next(&it)) { + ent = ngtcp2_ksl_it_get(&it); + CU_ASSERT(ent->hd.pkt_num != pkt_num); + } +} + +void test_ngtcp2_rtb_recv_ack(void) { + ngtcp2_rtb rtb; + const ngtcp2_mem *mem = ngtcp2_mem_default(); + ngtcp2_max_frame mfr; + ngtcp2_ack *fr = &mfr.ackfr.ack; + ngtcp2_ack_range *ranges; + ngtcp2_log log; + ngtcp2_conn_stat cstat; + ngtcp2_cc cc; + ngtcp2_pkt_hd hd; + ngtcp2_ssize num_acked; + ngtcp2_strm crypto; + const ngtcp2_pktns_id pktns_id = NGTCP2_PKTNS_ID_HANDSHAKE; + ngtcp2_rst rst; + ngtcp2_objalloc frc_objalloc; + ngtcp2_objalloc rtb_entry_objalloc; + + ngtcp2_objalloc_init(&frc_objalloc, 1024, mem); + ngtcp2_objalloc_init(&rtb_entry_objalloc, 1024, mem); + + ngtcp2_strm_init(&crypto, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, NULL, &frc_objalloc, + mem); + ngtcp2_log_init(&log, NULL, NULL, 0, NULL); + ngtcp2_pkt_hd_init(&hd, NGTCP2_PKT_FLAG_NONE, NGTCP2_PKT_1RTT, NULL, NULL, 0, + 1, NGTCP2_PROTO_VER_V1, 0); + + /* no ack block */ + conn_stat_init(&cstat); + ngtcp2_rst_init(&rst); + ngtcp2_cc_reno_cc_init(&cc, &log, mem); + ngtcp2_rtb_init(&rtb, pktns_id, &crypto, &rst, &cc, &log, NULL, + &rtb_entry_objalloc, &frc_objalloc, mem); + setup_rtb_fixture(&rtb, &cstat, &rtb_entry_objalloc); + + CU_ASSERT(67 == ngtcp2_ksl_len(&rtb.ents)); + + fr->largest_ack = 446; + fr->first_ack_range = 1; + fr->rangecnt = 0; + + num_acked = + ngtcp2_rtb_recv_ack(&rtb, fr, &cstat, NULL, NULL, 1000000009, 1000000009); + + CU_ASSERT(2 == num_acked); + CU_ASSERT(65 == ngtcp2_ksl_len(&rtb.ents)); + assert_rtb_entry_not_found(&rtb, 446); + assert_rtb_entry_not_found(&rtb, 445); + + ngtcp2_rtb_free(&rtb); + ngtcp2_cc_reno_cc_free(&cc, mem); + + /* with ack block */ + conn_stat_init(&cstat); + ngtcp2_cc_reno_cc_init(&cc, &log, mem); + ngtcp2_rtb_init(&rtb, pktns_id, &crypto, &rst, &cc, &log, NULL, + &rtb_entry_objalloc, &frc_objalloc, mem); + setup_rtb_fixture(&rtb, &cstat, &rtb_entry_objalloc); + + fr->largest_ack = 441; + fr->first_ack_range = 3; /* (441), (440), 439, 438 */ + fr->rangecnt = 2; + ranges = fr->ranges; + ranges[0].gap = 253; + ranges[0].len = 0; /* (183) */ + ranges[1].gap = 1; /* 182, 181 */ + ranges[1].len = 1; /* (180), 179 */ + + num_acked = + ngtcp2_rtb_recv_ack(&rtb, fr, &cstat, NULL, NULL, 1000000009, 1000000009); + + CU_ASSERT(4 == num_acked); + CU_ASSERT(63 == ngtcp2_ksl_len(&rtb.ents)); + CU_ASSERT(441 == rtb.largest_acked_tx_pkt_num); + assert_rtb_entry_not_found(&rtb, 441); + assert_rtb_entry_not_found(&rtb, 440); + assert_rtb_entry_not_found(&rtb, 183); + assert_rtb_entry_not_found(&rtb, 180); + + ngtcp2_rtb_free(&rtb); + ngtcp2_cc_reno_cc_free(&cc, mem); + + /* gap+len points to pkt_num 0 */ + conn_stat_init(&cstat); + ngtcp2_cc_reno_cc_init(&cc, &log, mem); + ngtcp2_rtb_init(&rtb, pktns_id, &crypto, &rst, &cc, &log, NULL, + &rtb_entry_objalloc, &frc_objalloc, mem); + add_rtb_entry_range(&rtb, 0, 1, &cstat, &rtb_entry_objalloc); + + fr->largest_ack = 250; + fr->first_ack_range = 0; + fr->rangecnt = 1; + fr->ranges[0].gap = 248; + fr->ranges[0].len = 0; + + num_acked = + ngtcp2_rtb_recv_ack(&rtb, fr, &cstat, NULL, NULL, 1000000009, 1000000009); + + CU_ASSERT(1 == num_acked); + assert_rtb_entry_not_found(&rtb, 0); + + ngtcp2_rtb_free(&rtb); + ngtcp2_cc_reno_cc_free(&cc, mem); + + /* pkt_num = 0 (first ack block) */ + conn_stat_init(&cstat); + ngtcp2_cc_reno_cc_init(&cc, &log, mem); + ngtcp2_rtb_init(&rtb, pktns_id, &crypto, &rst, &cc, &log, NULL, + &rtb_entry_objalloc, &frc_objalloc, mem); + add_rtb_entry_range(&rtb, 0, 1, &cstat, &rtb_entry_objalloc); + + fr->largest_ack = 0; + fr->first_ack_range = 0; + fr->rangecnt = 0; + + num_acked = + ngtcp2_rtb_recv_ack(&rtb, fr, &cstat, NULL, NULL, 1000000009, 1000000009); + + CU_ASSERT(1 == num_acked); + assert_rtb_entry_not_found(&rtb, 0); + + ngtcp2_rtb_free(&rtb); + ngtcp2_cc_reno_cc_free(&cc, mem); + + /* pkt_num = 0 */ + conn_stat_init(&cstat); + ngtcp2_cc_reno_cc_init(&cc, &log, mem); + ngtcp2_rtb_init(&rtb, pktns_id, &crypto, &rst, &cc, &log, NULL, + &rtb_entry_objalloc, &frc_objalloc, mem); + add_rtb_entry_range(&rtb, 0, 1, &cstat, &rtb_entry_objalloc); + + fr->largest_ack = 2; + fr->first_ack_range = 0; + fr->rangecnt = 1; + fr->ranges[0].gap = 0; + fr->ranges[0].len = 0; + + num_acked = + ngtcp2_rtb_recv_ack(&rtb, fr, &cstat, NULL, NULL, 1000000009, 1000000009); + + CU_ASSERT(1 == num_acked); + assert_rtb_entry_not_found(&rtb, 0); + + ngtcp2_rtb_free(&rtb); + ngtcp2_cc_reno_cc_free(&cc, mem); + ngtcp2_strm_free(&crypto); + + ngtcp2_objalloc_free(&rtb_entry_objalloc); + ngtcp2_objalloc_free(&frc_objalloc); +} + +void test_ngtcp2_rtb_lost_pkt_ts(void) { + ngtcp2_rtb rtb; + const ngtcp2_pktns_id pktns_id = NGTCP2_PKTNS_ID_APPLICATION; + ngtcp2_strm crypto; + ngtcp2_log log; + const ngtcp2_mem *mem = ngtcp2_mem_default(); + ngtcp2_cc cc; + ngtcp2_rst rst; + ngtcp2_conn_stat cstat; + ngtcp2_ksl_it it; + ngtcp2_rtb_entry *ent; + ngtcp2_objalloc frc_objalloc; + ngtcp2_objalloc rtb_entry_objalloc; + + ngtcp2_objalloc_init(&frc_objalloc, 1024, mem); + ngtcp2_objalloc_init(&rtb_entry_objalloc, 1024, mem); + + ngtcp2_strm_init(&crypto, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, NULL, &frc_objalloc, + mem); + ngtcp2_log_init(&log, NULL, NULL, 0, NULL); + + conn_stat_init(&cstat); + ngtcp2_rst_init(&rst); + ngtcp2_cc_reno_cc_init(&cc, &log, mem); + ngtcp2_rtb_init(&rtb, pktns_id, &crypto, &rst, &cc, &log, NULL, + &rtb_entry_objalloc, &frc_objalloc, mem); + + add_rtb_entry_range(&rtb, 0, 1, &cstat, &rtb_entry_objalloc); + + CU_ASSERT(UINT64_MAX == ngtcp2_rtb_lost_pkt_ts(&rtb)); + + it = ngtcp2_ksl_end(&rtb.ents); + ngtcp2_ksl_it_prev(&it); + ent = ngtcp2_ksl_it_get(&it); + ent->flags |= NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED; + ent->lost_ts = 16777217; + + CU_ASSERT(16777217 == ngtcp2_rtb_lost_pkt_ts(&rtb)); + + ngtcp2_rtb_free(&rtb); + ngtcp2_cc_reno_cc_free(&cc, mem); + ngtcp2_strm_free(&crypto); + + ngtcp2_objalloc_free(&rtb_entry_objalloc); + ngtcp2_objalloc_free(&frc_objalloc); +} + +void test_ngtcp2_rtb_remove_expired_lost_pkt(void) { + ngtcp2_rtb rtb; + const ngtcp2_pktns_id pktns_id = NGTCP2_PKTNS_ID_APPLICATION; + ngtcp2_strm crypto; + ngtcp2_log log; + const ngtcp2_mem *mem = ngtcp2_mem_default(); + ngtcp2_cc cc; + ngtcp2_rst rst; + ngtcp2_conn_stat cstat; + ngtcp2_ksl_it it; + ngtcp2_rtb_entry *ent; + size_t i; + ngtcp2_objalloc frc_objalloc; + ngtcp2_objalloc rtb_entry_objalloc; + + ngtcp2_objalloc_init(&frc_objalloc, 1024, mem); + ngtcp2_objalloc_init(&rtb_entry_objalloc, 1024, mem); + + ngtcp2_strm_init(&crypto, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, NULL, &frc_objalloc, + mem); + ngtcp2_log_init(&log, NULL, NULL, 0, NULL); + + conn_stat_init(&cstat); + ngtcp2_rst_init(&rst); + ngtcp2_cc_reno_cc_init(&cc, &log, mem); + ngtcp2_rtb_init(&rtb, pktns_id, &crypto, &rst, &cc, &log, NULL, + &rtb_entry_objalloc, &frc_objalloc, mem); + + add_rtb_entry_range(&rtb, 0, 7, &cstat, &rtb_entry_objalloc); + + it = ngtcp2_ksl_end(&rtb.ents); + + for (i = 0; i < 5; ++i) { + ngtcp2_ksl_it_prev(&it); + ent = ngtcp2_ksl_it_get(&it); + ent->flags |= NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED; + ent->lost_ts = 16777217 + i; + } + + ngtcp2_rtb_remove_expired_lost_pkt(&rtb, 1, 16777219); + + CU_ASSERT(5 == ngtcp2_ksl_len(&rtb.ents)); + + ngtcp2_rtb_remove_expired_lost_pkt(&rtb, 1, 16777223); + + CU_ASSERT(2 == ngtcp2_ksl_len(&rtb.ents)); + + ngtcp2_rtb_free(&rtb); + ngtcp2_cc_reno_cc_free(&cc, mem); + ngtcp2_strm_free(&crypto); + + ngtcp2_objalloc_free(&rtb_entry_objalloc); + ngtcp2_objalloc_free(&frc_objalloc); +} + +void test_ngtcp2_rtb_remove_excessive_lost_pkt(void) { + ngtcp2_rtb rtb; + const ngtcp2_pktns_id pktns_id = NGTCP2_PKTNS_ID_APPLICATION; + ngtcp2_strm crypto; + ngtcp2_log log; + const ngtcp2_mem *mem = ngtcp2_mem_default(); + ngtcp2_cc cc; + ngtcp2_rst rst; + ngtcp2_conn_stat cstat; + ngtcp2_ksl_it it; + ngtcp2_rtb_entry *ent; + size_t i; + ngtcp2_objalloc frc_objalloc; + ngtcp2_objalloc rtb_entry_objalloc; + + ngtcp2_objalloc_init(&frc_objalloc, 1024, mem); + ngtcp2_objalloc_init(&rtb_entry_objalloc, 1024, mem); + + ngtcp2_strm_init(&crypto, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, NULL, &frc_objalloc, + mem); + ngtcp2_log_init(&log, NULL, NULL, 0, NULL); + + conn_stat_init(&cstat); + ngtcp2_rst_init(&rst); + ngtcp2_cc_reno_cc_init(&cc, &log, mem); + ngtcp2_rtb_init(&rtb, pktns_id, &crypto, &rst, &cc, &log, NULL, + &rtb_entry_objalloc, &frc_objalloc, mem); + + add_rtb_entry_range(&rtb, 0, 7, &cstat, &rtb_entry_objalloc); + + it = ngtcp2_ksl_end(&rtb.ents); + + for (i = 0; i < 5; ++i) { + ngtcp2_ksl_it_prev(&it); + ent = ngtcp2_ksl_it_get(&it); + ent->flags |= NGTCP2_RTB_ENTRY_FLAG_LOST_RETRANSMITTED; + ent->lost_ts = 16777217; + ++rtb.num_lost_pkts; + } + + ngtcp2_rtb_remove_excessive_lost_pkt(&rtb, 2); + + CU_ASSERT(4 == ngtcp2_ksl_len(&rtb.ents)); + + ngtcp2_rtb_free(&rtb); + ngtcp2_cc_reno_cc_free(&cc, mem); + ngtcp2_strm_free(&crypto); + + ngtcp2_objalloc_free(&rtb_entry_objalloc); + ngtcp2_objalloc_free(&frc_objalloc); +} diff --git a/tests/ngtcp2_rtb_test.h b/tests/ngtcp2_rtb_test.h new file mode 100644 index 0000000..f880e1e --- /dev/null +++ b/tests/ngtcp2_rtb_test.h @@ -0,0 +1,38 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_RTB_TEST_H +#define NGTCP2_RTB_TEST_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +void test_ngtcp2_rtb_add(void); +void test_ngtcp2_rtb_recv_ack(void); +void test_ngtcp2_rtb_lost_pkt_ts(void); +void test_ngtcp2_rtb_remove_expired_lost_pkt(void); +void test_ngtcp2_rtb_remove_excessive_lost_pkt(void); + +#endif /* NGTCP2_RTB_TEST_H */ diff --git a/tests/ngtcp2_str_test.c b/tests/ngtcp2_str_test.c new file mode 100644 index 0000000..3d5619d --- /dev/null +++ b/tests/ngtcp2_str_test.c @@ -0,0 +1,96 @@ +/* + * ngtcp2 + * + * Copyright (c) 2020 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_str_test.h" + +#include <CUnit/CUnit.h> + +#include "ngtcp2_str.h" +#include "ngtcp2_test_helper.h" + +void test_ngtcp2_encode_ipv4(void) { + uint8_t buf[16]; + + CU_ASSERT(0 == strcmp("192.168.0.1", + (const char *)ngtcp2_encode_ipv4( + buf, (const uint8_t *)"\xc0\xa8\x00\x01"))); + CU_ASSERT(0 == + strcmp("127.0.0.1", (const char *)ngtcp2_encode_ipv4( + buf, (const uint8_t *)"\x7f\x00\x00\x01"))); +} + +void test_ngtcp2_encode_ipv6(void) { + uint8_t buf[32 + 7 + 1]; + + CU_ASSERT( + 0 == + strcmp("2001:db8::2:1", + (const char *)ngtcp2_encode_ipv6( + buf, (const uint8_t *)"\x20\x01\x0d\xb8\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x02\x00\x01"))); + CU_ASSERT( + 0 == + strcmp("2001:db8:0:1:1:1:1:1", + (const char *)ngtcp2_encode_ipv6( + buf, (const uint8_t *)"\x20\x01\x0d\xb8\x00\x00\x00\x01\x00" + "\x01\x00\x01\x00\x01\x00\x01"))); + CU_ASSERT( + 0 == + strcmp("2001:db8::1:0:0:1", + (const char *)ngtcp2_encode_ipv6( + buf, (const uint8_t *)"\x20\x01\x0d\xb8\x00\x00\x00\x00\x00" + "\x01\x00\x00\x00\x00\x00\x01"))); + CU_ASSERT( + 0 == + strcmp("2001:db8::8:800:200c:417a", + (const char *)ngtcp2_encode_ipv6( + buf, (const uint8_t *)"\x20\x01\x0d\xb8\x00\x00\x00\x00\x00" + "\x08\x08\x00\x20\x0C\x41\x7a"))); + CU_ASSERT( + 0 == + strcmp("ff01::101", + (const char *)ngtcp2_encode_ipv6( + buf, (const uint8_t *)"\xff\x01\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x01\x01"))); + CU_ASSERT( + 0 == + strcmp("::1", + (const char *)ngtcp2_encode_ipv6( + buf, (const uint8_t *)"\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x01"))); + CU_ASSERT( + 0 == + strcmp("::", + (const char *)ngtcp2_encode_ipv6( + buf, (const uint8_t *)"\x00\x00\x00\x00\x00\x00\x00\x00\x00" + "\x00\x00\x00\x00\x00\x00\x00"))); +} + +void test_ngtcp2_get_bytes(void) { + const uint8_t src[] = {'f', 'o', 'o', 'b', 'a', 'r'}; + uint8_t dest[256]; + + CU_ASSERT(src + sizeof(src) == ngtcp2_get_bytes(dest, src, sizeof(src))); + CU_ASSERT(0 == memcmp(src, dest, sizeof(src))); +} diff --git a/tests/ngtcp2_str_test.h b/tests/ngtcp2_str_test.h new file mode 100644 index 0000000..9d67c9f --- /dev/null +++ b/tests/ngtcp2_str_test.h @@ -0,0 +1,36 @@ +/* + * ngtcp2 + * + * Copyright (c) 2020 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_STR_TEST_H +#define NGTCP2_STR_TEST_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +void test_ngtcp2_encode_ipv4(void); +void test_ngtcp2_encode_ipv6(void); +void test_ngtcp2_get_bytes(void); + +#endif /* NGTCP2_STR_TEST_H */ diff --git a/tests/ngtcp2_strm_test.c b/tests/ngtcp2_strm_test.c new file mode 100644 index 0000000..b2a71c0 --- /dev/null +++ b/tests/ngtcp2_strm_test.c @@ -0,0 +1,575 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_strm_test.h" + +#include <CUnit/CUnit.h> + +#include "ngtcp2_strm.h" +#include "ngtcp2_test_helper.h" +#include "ngtcp2_vec.h" + +static uint8_t nulldata[1024]; + +static void setup_strm_streamfrq_fixture(ngtcp2_strm *strm, + ngtcp2_objalloc *frc_objalloc, + const ngtcp2_mem *mem) { + ngtcp2_frame_chain *frc; + ngtcp2_vec *data; + + ngtcp2_strm_init(strm, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, NULL, frc_objalloc, + mem); + + ngtcp2_frame_chain_stream_datacnt_objalloc_new(&frc, 2, frc_objalloc, mem); + frc->fr.stream.type = NGTCP2_FRAME_STREAM; + frc->fr.stream.fin = 0; + frc->fr.stream.offset = 0; + frc->fr.stream.datacnt = 2; + data = frc->fr.stream.data; + data[0].len = 11; + data[0].base = nulldata; + data[1].len = 19; + data[1].base = nulldata + 11; + + ngtcp2_strm_streamfrq_push(strm, frc); + + ngtcp2_frame_chain_stream_datacnt_objalloc_new(&frc, 2, frc_objalloc, mem); + frc->fr.stream.type = NGTCP2_FRAME_STREAM; + frc->fr.stream.fin = 0; + frc->fr.stream.offset = 30; + frc->fr.stream.datacnt = 2; + data = frc->fr.stream.data; + data[0].len = 17; + data[0].base = nulldata + 30; + data[1].len = 29; + data[1].base = nulldata + 30 + 17; + + ngtcp2_strm_streamfrq_push(strm, frc); + + ngtcp2_frame_chain_stream_datacnt_objalloc_new(&frc, 2, frc_objalloc, mem); + frc->fr.stream.type = NGTCP2_FRAME_STREAM; + frc->fr.stream.fin = 0; + frc->fr.stream.offset = 76; + frc->fr.stream.datacnt = 2; + data = frc->fr.stream.data; + data[0].len = 31; + data[0].base = nulldata + 256; + data[1].len = 1; + data[1].base = nulldata + 512; + + ngtcp2_strm_streamfrq_push(strm, frc); +} + +void test_ngtcp2_strm_streamfrq_pop(void) { + ngtcp2_strm strm; + ngtcp2_frame_chain *frc; + const ngtcp2_mem *mem = ngtcp2_mem_default(); + int rv; + ngtcp2_vec *data; + ngtcp2_objalloc frc_objalloc; + + ngtcp2_objalloc_init(&frc_objalloc, 1024, mem); + + /* Get first chain */ + setup_strm_streamfrq_fixture(&strm, &frc_objalloc, mem); + + frc = NULL; + rv = ngtcp2_strm_streamfrq_pop(&strm, &frc, 30); + + CU_ASSERT(0 == rv); + CU_ASSERT(2 == frc->fr.stream.datacnt); + + data = frc->fr.stream.data; + + CU_ASSERT(11 == data[0].len); + CU_ASSERT(19 == data[1].len); + CU_ASSERT(2 == ngtcp2_ksl_len(strm.tx.streamfrq)); + + ngtcp2_frame_chain_objalloc_del(frc, &frc_objalloc, mem); + ngtcp2_strm_free(&strm); + + /* Get merged chain */ + setup_strm_streamfrq_fixture(&strm, &frc_objalloc, mem); + + frc = NULL; + rv = ngtcp2_strm_streamfrq_pop(&strm, &frc, 76); + + CU_ASSERT(0 == rv); + CU_ASSERT(2 == frc->fr.stream.datacnt); + + data = frc->fr.stream.data; + + CU_ASSERT(11 == data[0].len); + CU_ASSERT(19 + 46 == data[1].len); + CU_ASSERT(1 == ngtcp2_ksl_len(strm.tx.streamfrq)); + + ngtcp2_frame_chain_objalloc_del(frc, &frc_objalloc, mem); + ngtcp2_strm_free(&strm); + + /* Get merged chain partially */ + setup_strm_streamfrq_fixture(&strm, &frc_objalloc, mem); + + frc = NULL; + rv = ngtcp2_strm_streamfrq_pop(&strm, &frc, 75); + + CU_ASSERT(0 == rv); + CU_ASSERT(2 == frc->fr.stream.datacnt); + + data = frc->fr.stream.data; + + CU_ASSERT(11 == data[0].len); + CU_ASSERT(19 + 45 == data[1].len); + CU_ASSERT(2 == ngtcp2_ksl_len(strm.tx.streamfrq)); + + ngtcp2_frame_chain_objalloc_del(frc, &frc_objalloc, mem); + + frc = NULL; + rv = ngtcp2_strm_streamfrq_pop(&strm, &frc, 1); + + CU_ASSERT(0 == rv); + CU_ASSERT(75 == frc->fr.stream.offset); + CU_ASSERT(1 == frc->fr.stream.datacnt); + CU_ASSERT(1 == frc->fr.stream.data[0].len); + CU_ASSERT(nulldata + 30 + 17 + 28 == frc->fr.stream.data[0].base); + + ngtcp2_frame_chain_objalloc_del(frc, &frc_objalloc, mem); + ngtcp2_strm_free(&strm); + + /* Not continuous merge */ + setup_strm_streamfrq_fixture(&strm, &frc_objalloc, mem); + + frc = NULL; + rv = ngtcp2_strm_streamfrq_pop(&strm, &frc, 77); + + CU_ASSERT(0 == rv); + CU_ASSERT(3 == frc->fr.stream.datacnt); + + data = frc->fr.stream.data; + + CU_ASSERT(11 == data[0].len); + CU_ASSERT(19 + 46 == data[1].len); + CU_ASSERT(1 == data[2].len); + CU_ASSERT(nulldata + 256 == data[2].base); + CU_ASSERT(1 == ngtcp2_ksl_len(strm.tx.streamfrq)); + + ngtcp2_frame_chain_objalloc_del(frc, &frc_objalloc, mem); + + frc = NULL; + rv = ngtcp2_strm_streamfrq_pop(&strm, &frc, 1024); + + CU_ASSERT(0 == rv); + CU_ASSERT(77 == frc->fr.stream.offset); + CU_ASSERT(2 == frc->fr.stream.datacnt); + + data = frc->fr.stream.data; + + CU_ASSERT(30 == data[0].len); + CU_ASSERT(nulldata + 256 + 1 == data[0].base); + + ngtcp2_frame_chain_objalloc_del(frc, &frc_objalloc, mem); + ngtcp2_strm_free(&strm); + + /* split; continuous */ + setup_strm_streamfrq_fixture(&strm, &frc_objalloc, mem); + + frc = NULL; + rv = ngtcp2_strm_streamfrq_pop(&strm, &frc, 12); + + CU_ASSERT(0 == rv); + CU_ASSERT(0 == frc->fr.stream.offset); + CU_ASSERT(2 == frc->fr.stream.datacnt); + + data = frc->fr.stream.data; + + CU_ASSERT(11 == data[0].len); + CU_ASSERT(nulldata == data[0].base); + CU_ASSERT(1 == data[1].len); + CU_ASSERT(nulldata + 11 == data[1].base); + + ngtcp2_frame_chain_objalloc_del(frc, &frc_objalloc, mem); + + frc = NULL; + rv = ngtcp2_strm_streamfrq_pop(&strm, &frc, 1024); + + CU_ASSERT(0 == rv); + CU_ASSERT(12 == frc->fr.stream.offset); + CU_ASSERT(3 == frc->fr.stream.datacnt); + + data = frc->fr.stream.data; + + CU_ASSERT(64 == data[0].len); + CU_ASSERT(nulldata + 12 == data[0].base); + CU_ASSERT(31 == data[1].len); + CU_ASSERT(nulldata + 256 == data[1].base); + CU_ASSERT(1 == data[2].len); + CU_ASSERT(nulldata + 512 == data[2].base); + + ngtcp2_frame_chain_objalloc_del(frc, &frc_objalloc, mem); + ngtcp2_strm_free(&strm); + + /* offset gap */ + ngtcp2_strm_init(&strm, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, NULL, &frc_objalloc, + mem); + + ngtcp2_frame_chain_stream_datacnt_objalloc_new(&frc, 1, &frc_objalloc, mem); + frc->fr.stream.type = NGTCP2_FRAME_STREAM; + frc->fr.stream.fin = 0; + frc->fr.stream.offset = 0; + frc->fr.stream.datacnt = 1; + data = frc->fr.stream.data; + data[0].len = 11; + data[0].base = nulldata; + + ngtcp2_strm_streamfrq_push(&strm, frc); + + ngtcp2_frame_chain_stream_datacnt_objalloc_new(&frc, 1, &frc_objalloc, mem); + frc->fr.stream.type = NGTCP2_FRAME_STREAM; + frc->fr.stream.fin = 0; + frc->fr.stream.offset = 30; + frc->fr.stream.datacnt = 1; + data = frc->fr.stream.data; + data[0].len = 17; + data[0].base = nulldata + 30; + + ngtcp2_strm_streamfrq_push(&strm, frc); + + frc = NULL; + rv = ngtcp2_strm_streamfrq_pop(&strm, &frc, 1024); + + CU_ASSERT(0 == rv); + CU_ASSERT(1 == frc->fr.stream.datacnt); + CU_ASSERT(11 == frc->fr.stream.data[0].len); + CU_ASSERT(1 == ngtcp2_ksl_len(strm.tx.streamfrq)); + + ngtcp2_frame_chain_objalloc_del(frc, &frc_objalloc, mem); + ngtcp2_strm_free(&strm); + + /* fin */ + ngtcp2_strm_init(&strm, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, NULL, &frc_objalloc, + mem); + + ngtcp2_frame_chain_stream_datacnt_objalloc_new(&frc, 1, &frc_objalloc, mem); + frc->fr.stream.type = NGTCP2_FRAME_STREAM; + frc->fr.stream.fin = 0; + frc->fr.stream.offset = 0; + frc->fr.stream.datacnt = 1; + data = frc->fr.stream.data; + data[0].len = 11; + data[0].base = nulldata; + + ngtcp2_strm_streamfrq_push(&strm, frc); + + ngtcp2_frame_chain_stream_datacnt_objalloc_new(&frc, 0, &frc_objalloc, mem); + frc->fr.stream.type = NGTCP2_FRAME_STREAM; + frc->fr.stream.fin = 1; + frc->fr.stream.offset = 11; + frc->fr.stream.datacnt = 0; + + ngtcp2_strm_streamfrq_push(&strm, frc); + + frc = NULL; + rv = ngtcp2_strm_streamfrq_pop(&strm, &frc, 1024); + + CU_ASSERT(0 == rv); + CU_ASSERT(1 == frc->fr.stream.fin); + CU_ASSERT(1 == frc->fr.stream.datacnt); + + ngtcp2_frame_chain_objalloc_del(frc, &frc_objalloc, mem); + ngtcp2_strm_free(&strm); + + /* left == 0 and there is outstanding data */ + setup_strm_streamfrq_fixture(&strm, &frc_objalloc, mem); + + frc = NULL; + rv = ngtcp2_strm_streamfrq_pop(&strm, &frc, 0); + + CU_ASSERT(0 == rv); + CU_ASSERT(NULL == frc); + + ngtcp2_strm_free(&strm); + + ngtcp2_objalloc_free(&frc_objalloc); +} + +void test_ngtcp2_strm_streamfrq_unacked_offset(void) { + ngtcp2_strm strm; + ngtcp2_frame_chain *frc; + const ngtcp2_mem *mem = ngtcp2_mem_default(); + ngtcp2_vec *data; + ngtcp2_objalloc frc_objalloc; + + ngtcp2_objalloc_init(&frc_objalloc, 1024, mem); + + /* Everything acknowledged including fin */ + ngtcp2_strm_init(&strm, 0, NGTCP2_STRM_FLAG_FIN_ACKED, 0, 0, NULL, + &frc_objalloc, mem); + + ngtcp2_frame_chain_stream_datacnt_objalloc_new(&frc, 1, &frc_objalloc, mem); + frc->fr.stream.type = NGTCP2_FRAME_STREAM; + frc->fr.stream.fin = 0; + frc->fr.stream.offset = 0; + frc->fr.stream.datacnt = 1; + data = frc->fr.stream.data; + data[0].len = 17; + data[0].base = nulldata; + + ngtcp2_strm_streamfrq_push(&strm, frc); + + ngtcp2_frame_chain_stream_datacnt_objalloc_new(&frc, 1, &frc_objalloc, mem); + frc->fr.stream.type = NGTCP2_FRAME_STREAM; + frc->fr.stream.fin = 1; + frc->fr.stream.offset = 443; + frc->fr.stream.datacnt = 1; + data = frc->fr.stream.data; + data[0].len = 971; + data[0].base = nulldata; + + ngtcp2_strm_streamfrq_push(&strm, frc); + + ngtcp2_strm_ack_data(&strm, 0, 443 + 971); + + CU_ASSERT((uint64_t)-1 == ngtcp2_strm_streamfrq_unacked_offset(&strm)); + + ngtcp2_strm_free(&strm); + + /* Everything acknowledged but fin */ + ngtcp2_strm_init(&strm, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, NULL, &frc_objalloc, + mem); + + ngtcp2_frame_chain_stream_datacnt_objalloc_new(&frc, 1, &frc_objalloc, mem); + frc->fr.stream.type = NGTCP2_FRAME_STREAM; + frc->fr.stream.fin = 0; + frc->fr.stream.offset = 0; + frc->fr.stream.datacnt = 1; + data = frc->fr.stream.data; + data[0].len = 17; + data[0].base = nulldata; + + ngtcp2_strm_streamfrq_push(&strm, frc); + + ngtcp2_frame_chain_stream_datacnt_objalloc_new(&frc, 1, &frc_objalloc, mem); + frc->fr.stream.type = NGTCP2_FRAME_STREAM; + frc->fr.stream.fin = 1; + frc->fr.stream.offset = 443; + frc->fr.stream.datacnt = 1; + data = frc->fr.stream.data; + data[0].len = 971; + data[0].base = nulldata; + + ngtcp2_strm_streamfrq_push(&strm, frc); + + ngtcp2_strm_ack_data(&strm, 0, 443 + 971); + + CU_ASSERT(443 + 971 == ngtcp2_strm_streamfrq_unacked_offset(&strm)); + + ngtcp2_strm_free(&strm); + + /* Unacked gap starts in the middle of stream to resend */ + ngtcp2_strm_init(&strm, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, NULL, &frc_objalloc, + mem); + + ngtcp2_frame_chain_stream_datacnt_objalloc_new(&frc, 1, &frc_objalloc, mem); + frc->fr.stream.type = NGTCP2_FRAME_STREAM; + frc->fr.stream.fin = 0; + frc->fr.stream.offset = 0; + frc->fr.stream.datacnt = 1; + data = frc->fr.stream.data; + data[0].len = 971; + data[0].base = nulldata; + + ngtcp2_strm_streamfrq_push(&strm, frc); + + ngtcp2_strm_ack_data(&strm, 0, 443); + + CU_ASSERT(443 == ngtcp2_strm_streamfrq_unacked_offset(&strm)); + + ngtcp2_strm_free(&strm); + + /* Unacked gap starts after stream to resend */ + ngtcp2_strm_init(&strm, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, NULL, &frc_objalloc, + mem); + + ngtcp2_frame_chain_stream_datacnt_objalloc_new(&frc, 1, &frc_objalloc, mem); + frc->fr.stream.type = NGTCP2_FRAME_STREAM; + frc->fr.stream.fin = 0; + frc->fr.stream.offset = 0; + frc->fr.stream.datacnt = 1; + data = frc->fr.stream.data; + data[0].len = 971; + data[0].base = nulldata; + + ngtcp2_strm_streamfrq_push(&strm, frc); + + ngtcp2_strm_ack_data(&strm, 0, 971); + + CU_ASSERT((uint64_t)-1 == ngtcp2_strm_streamfrq_unacked_offset(&strm)); + + ngtcp2_strm_free(&strm); + + /* Unacked gap and stream overlap and gap starts before stream */ + ngtcp2_strm_init(&strm, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, NULL, &frc_objalloc, + mem); + + ngtcp2_frame_chain_stream_datacnt_objalloc_new(&frc, 1, &frc_objalloc, mem); + frc->fr.stream.type = NGTCP2_FRAME_STREAM; + frc->fr.stream.fin = 0; + frc->fr.stream.offset = 977; + frc->fr.stream.datacnt = 1; + data = frc->fr.stream.data; + data[0].len = 971; + data[0].base = nulldata; + + ngtcp2_strm_streamfrq_push(&strm, frc); + + ngtcp2_strm_ack_data(&strm, 0, 971); + + CU_ASSERT(977 == ngtcp2_strm_streamfrq_unacked_offset(&strm)); + + ngtcp2_strm_free(&strm); + + ngtcp2_objalloc_free(&frc_objalloc); +} + +void test_ngtcp2_strm_streamfrq_unacked_pop(void) { + ngtcp2_strm strm; + ngtcp2_frame_chain *frc; + const ngtcp2_mem *mem = ngtcp2_mem_default(); + ngtcp2_vec *data; + int rv; + ngtcp2_objalloc frc_objalloc; + + ngtcp2_objalloc_init(&frc_objalloc, 1024, mem); + + /* Everything acknowledged including fin */ + ngtcp2_strm_init(&strm, 0, NGTCP2_STRM_FLAG_FIN_ACKED, 0, 0, NULL, + &frc_objalloc, mem); + + ngtcp2_frame_chain_stream_datacnt_objalloc_new(&frc, 1, &frc_objalloc, mem); + frc->fr.stream.type = NGTCP2_FRAME_STREAM; + frc->fr.stream.fin = 0; + frc->fr.stream.offset = 307; + frc->fr.stream.datacnt = 1; + data = frc->fr.stream.data; + data[0].len = 149; + data[0].base = nulldata; + + ngtcp2_strm_streamfrq_push(&strm, frc); + + ngtcp2_frame_chain_stream_datacnt_objalloc_new(&frc, 1, &frc_objalloc, mem); + frc->fr.stream.type = NGTCP2_FRAME_STREAM; + frc->fr.stream.fin = 1; + frc->fr.stream.offset = 457; + frc->fr.stream.datacnt = 1; + data = frc->fr.stream.data; + data[0].len = 307; + data[0].base = nulldata; + + ngtcp2_strm_streamfrq_push(&strm, frc); + + ngtcp2_strm_ack_data(&strm, 0, 764); + + frc = NULL; + rv = ngtcp2_strm_streamfrq_pop(&strm, &frc, 1024); + + CU_ASSERT(0 == rv); + CU_ASSERT(NULL == frc); + + ngtcp2_strm_free(&strm); + + /* Everything acknowledged but fin */ + ngtcp2_strm_init(&strm, 0, NGTCP2_STRM_FLAG_NONE, 0, 0, NULL, &frc_objalloc, + mem); + + ngtcp2_frame_chain_stream_datacnt_objalloc_new(&frc, 1, &frc_objalloc, mem); + frc->fr.stream.type = NGTCP2_FRAME_STREAM; + frc->fr.stream.fin = 0; + frc->fr.stream.offset = 307; + frc->fr.stream.datacnt = 1; + data = frc->fr.stream.data; + data[0].len = 149; + data[0].base = nulldata; + + ngtcp2_strm_streamfrq_push(&strm, frc); + + ngtcp2_frame_chain_stream_datacnt_objalloc_new(&frc, 1, &frc_objalloc, mem); + frc->fr.stream.type = NGTCP2_FRAME_STREAM; + frc->fr.stream.fin = 1; + frc->fr.stream.offset = 457; + frc->fr.stream.datacnt = 1; + data = frc->fr.stream.data; + data[0].len = 307; + data[0].base = nulldata; + + ngtcp2_strm_streamfrq_push(&strm, frc); + + ngtcp2_strm_ack_data(&strm, 0, 764); + + frc = NULL; + rv = ngtcp2_strm_streamfrq_pop(&strm, &frc, 1024); + + CU_ASSERT(0 == rv); + CU_ASSERT(NGTCP2_FRAME_STREAM == frc->fr.type); + CU_ASSERT(1 == frc->fr.stream.fin); + CU_ASSERT(764 == frc->fr.stream.offset); + CU_ASSERT(0 == ngtcp2_vec_len(frc->fr.stream.data, frc->fr.stream.datacnt)); + + ngtcp2_frame_chain_objalloc_del(frc, &frc_objalloc, mem); + ngtcp2_strm_free(&strm); + + /* Remove leading acknowledged data */ + setup_strm_streamfrq_fixture(&strm, &frc_objalloc, mem); + + ngtcp2_strm_ack_data(&strm, 0, 12); + + rv = ngtcp2_strm_streamfrq_pop(&strm, &frc, 43); + + CU_ASSERT(0 == rv); + CU_ASSERT(NGTCP2_FRAME_STREAM == frc->fr.type); + CU_ASSERT(0 == frc->fr.stream.fin); + CU_ASSERT(12 == frc->fr.stream.offset); + CU_ASSERT(1 == frc->fr.stream.datacnt); + CU_ASSERT(43 == ngtcp2_vec_len(frc->fr.stream.data, frc->fr.stream.datacnt)); + + ngtcp2_frame_chain_objalloc_del(frc, &frc_objalloc, mem); + ngtcp2_strm_free(&strm); + + /* Creating a gap of acknowledged data */ + setup_strm_streamfrq_fixture(&strm, &frc_objalloc, mem); + + ngtcp2_strm_ack_data(&strm, 32, 1); + + rv = ngtcp2_strm_streamfrq_pop(&strm, &frc, 43); + + CU_ASSERT(0 == rv); + CU_ASSERT(NGTCP2_FRAME_STREAM == frc->fr.type); + CU_ASSERT(0 == frc->fr.stream.fin); + CU_ASSERT(0 == frc->fr.stream.offset); + CU_ASSERT(2 == frc->fr.stream.datacnt); + CU_ASSERT(32 == ngtcp2_vec_len(frc->fr.stream.data, frc->fr.stream.datacnt)); + + ngtcp2_frame_chain_objalloc_del(frc, &frc_objalloc, mem); + ngtcp2_strm_free(&strm); + + ngtcp2_objalloc_free(&frc_objalloc); +} diff --git a/tests/ngtcp2_strm_test.h b/tests/ngtcp2_strm_test.h new file mode 100644 index 0000000..f94c423 --- /dev/null +++ b/tests/ngtcp2_strm_test.h @@ -0,0 +1,36 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_STRM_TEST_H +#define NGTCP2_STRM_TEST_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +void test_ngtcp2_strm_streamfrq_pop(void); +void test_ngtcp2_strm_streamfrq_unacked_offset(void); +void test_ngtcp2_strm_streamfrq_unacked_pop(void); + +#endif /* NGTCP2_STRM_TEST_H */ diff --git a/tests/ngtcp2_test_helper.c b/tests/ngtcp2_test_helper.c new file mode 100644 index 0000000..a29a943 --- /dev/null +++ b/tests/ngtcp2_test_helper.c @@ -0,0 +1,404 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_test_helper.h" + +#include <string.h> +#include <assert.h> + +#include "ngtcp2_conv.h" +#include "ngtcp2_pkt.h" +#include "ngtcp2_ppe.h" +#include "ngtcp2_vec.h" +#include "ngtcp2_net.h" + +size_t ngtcp2_t_encode_stream_frame(uint8_t *out, uint8_t flags, + uint64_t stream_id, uint64_t offset, + uint16_t datalen) { + uint8_t *p = out; + + if (offset) { + flags |= NGTCP2_STREAM_OFF_BIT; + } + *p++ = NGTCP2_FRAME_STREAM | flags; + + p = ngtcp2_put_uvarint(p, stream_id); + + if (offset) { + p = ngtcp2_put_uvarint(p, offset); + } + + if (flags & NGTCP2_STREAM_LEN_BIT) { + p = ngtcp2_put_uvarint(p, datalen); + } + + memset(p, 0, datalen); + p += datalen; + + return (size_t)(p - out); +} + +size_t ngtcp2_t_encode_ack_frame(uint8_t *out, uint64_t largest_ack, + uint64_t first_ack_blklen, uint64_t gap, + uint64_t ack_blklen) { + uint8_t *p = out; + + p = out; + + *p++ = NGTCP2_FRAME_ACK; + /* Largest Acknowledged */ + p = ngtcp2_put_uvarint(p, largest_ack); + /* ACK Delay */ + p = ngtcp2_put_uvarint(p, 0); + /* ACK Block Count */ + p = ngtcp2_put_uvarint(p, 1); + /* First ACK Block */ + p = ngtcp2_put_uvarint(p, first_ack_blklen); + /* Gap (1) */ + p = ngtcp2_put_uvarint(p, gap); + /* Additional ACK Block (1) */ + p = ngtcp2_put_uvarint(p, ack_blklen); + + return (size_t)(p - out); +} + +static int null_encrypt(uint8_t *dest, const ngtcp2_crypto_aead *aead, + const ngtcp2_crypto_aead_ctx *aead_ctx, + const uint8_t *plaintext, size_t plaintextlen, + const uint8_t *nonce, size_t noncelen, + const uint8_t *aad, size_t aadlen) { + (void)dest; + (void)aead; + (void)aead_ctx; + (void)plaintext; + (void)plaintextlen; + (void)nonce; + (void)noncelen; + (void)aad; + (void)aadlen; + memset(dest + plaintextlen, 0, NGTCP2_FAKE_AEAD_OVERHEAD); + return 0; +} + +static int null_hp_mask(uint8_t *dest, const ngtcp2_crypto_cipher *hp, + const ngtcp2_crypto_cipher_ctx *hp_ctx, + const uint8_t *sample) { + (void)hp; + (void)hp_ctx; + (void)sample; + memcpy(dest, NGTCP2_FAKE_HP_MASK, sizeof(NGTCP2_FAKE_HP_MASK) - 1); + return 0; +} + +size_t write_pkt(uint8_t *out, size_t outlen, const ngtcp2_cid *dcid, + int64_t pkt_num, ngtcp2_frame *fr, size_t frlen, + ngtcp2_crypto_km *ckm) { + return write_pkt_flags(out, outlen, NGTCP2_PKT_FLAG_NONE, dcid, pkt_num, fr, + frlen, ckm); +} + +size_t write_pkt_flags(uint8_t *out, size_t outlen, uint8_t flags, + const ngtcp2_cid *dcid, int64_t pkt_num, + ngtcp2_frame *fr, size_t frlen, ngtcp2_crypto_km *ckm) { + ngtcp2_crypto_cc cc; + ngtcp2_ppe ppe; + ngtcp2_pkt_hd hd; + int rv; + ngtcp2_ssize n; + size_t i; + + memset(&cc, 0, sizeof(cc)); + cc.encrypt = null_encrypt; + cc.hp_mask = null_hp_mask; + cc.ckm = ckm; + cc.aead.max_overhead = NGTCP2_FAKE_AEAD_OVERHEAD; + + ngtcp2_pkt_hd_init(&hd, flags, NGTCP2_PKT_1RTT, dcid, NULL, pkt_num, 4, + NGTCP2_PROTO_VER_V1, 0); + + ngtcp2_ppe_init(&ppe, out, outlen, &cc); + rv = ngtcp2_ppe_encode_hd(&ppe, &hd); + assert(0 == rv); + + for (i = 0; i < frlen; ++i, ++fr) { + rv = ngtcp2_ppe_encode_frame(&ppe, fr); + assert(0 == rv); + } + + n = ngtcp2_ppe_final(&ppe, NULL); + assert(n > 0); + + return (size_t)n; +} + +static size_t write_long_header_pkt_generic( + uint8_t *out, size_t outlen, uint8_t pkt_type, const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, int64_t pkt_num, uint32_t version, + const uint8_t *token, size_t tokenlen, ngtcp2_frame *fr, size_t frlen, + ngtcp2_crypto_km *ckm) { + ngtcp2_crypto_cc cc; + ngtcp2_ppe ppe; + ngtcp2_pkt_hd hd; + int rv; + ngtcp2_ssize n; + size_t i; + + memset(&cc, 0, sizeof(cc)); + cc.encrypt = null_encrypt; + cc.hp_mask = null_hp_mask; + cc.ckm = ckm; + switch (pkt_type) { + case NGTCP2_PKT_INITIAL: + cc.aead.max_overhead = NGTCP2_INITIAL_AEAD_OVERHEAD; + break; + case NGTCP2_PKT_HANDSHAKE: + case NGTCP2_PKT_0RTT: + cc.aead.max_overhead = NGTCP2_FAKE_AEAD_OVERHEAD; + break; + default: + assert(0); + } + + /* ngtcp2_pkt_encode_hd_long requires known QUIC version. If we + need to write unsupported version for testing purpose, just + pretend that it is QUIC v1 here and rewrite the version field + later. */ + ngtcp2_pkt_hd_init( + &hd, NGTCP2_PKT_FLAG_LONG_FORM, pkt_type, dcid, scid, pkt_num, 4, + version != NGTCP2_PROTO_VER_V1 && version != NGTCP2_PROTO_VER_V2_DRAFT + ? NGTCP2_PROTO_VER_V1 + : version, + 0); + + hd.token.base = (uint8_t *)token; + hd.token.len = tokenlen; + + ngtcp2_ppe_init(&ppe, out, outlen, &cc); + rv = ngtcp2_ppe_encode_hd(&ppe, &hd); + assert(0 == rv); + ngtcp2_put_uint32be(&out[1], version); + + for (i = 0; i < frlen; ++i, ++fr) { + rv = ngtcp2_ppe_encode_frame(&ppe, fr); + assert(0 == rv); + } + + n = ngtcp2_ppe_final(&ppe, NULL); + assert(n > 0); + return (size_t)n; +} + +size_t write_initial_pkt(uint8_t *out, size_t outlen, const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, int64_t pkt_num, + uint32_t version, const uint8_t *token, + size_t tokenlen, ngtcp2_frame *fr, size_t frlen, + ngtcp2_crypto_km *ckm) { + return write_long_header_pkt_generic(out, outlen, NGTCP2_PKT_INITIAL, dcid, + scid, pkt_num, version, token, tokenlen, + fr, frlen, ckm); +} + +size_t write_handshake_pkt(uint8_t *out, size_t outlen, const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, int64_t pkt_num, + uint32_t version, ngtcp2_frame *fr, size_t frlen, + ngtcp2_crypto_km *ckm) { + return write_long_header_pkt_generic(out, outlen, NGTCP2_PKT_HANDSHAKE, dcid, + scid, pkt_num, version, NULL, 0, fr, + frlen, ckm); +} + +size_t write_0rtt_pkt(uint8_t *out, size_t outlen, const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, int64_t pkt_num, uint32_t version, + ngtcp2_frame *fr, size_t frlen, ngtcp2_crypto_km *ckm) { + return write_long_header_pkt_generic(out, outlen, NGTCP2_PKT_0RTT, dcid, scid, + pkt_num, version, NULL, 0, fr, frlen, + ckm); +} + +ngtcp2_strm *open_stream(ngtcp2_conn *conn, int64_t stream_id) { + ngtcp2_strm *strm; + int rv; + (void)rv; + + strm = ngtcp2_objalloc_strm_get(&conn->strm_objalloc); + assert(strm); + + rv = ngtcp2_conn_init_stream(conn, strm, stream_id, NULL); + assert(0 == rv); + + return strm; +} + +size_t rtb_entry_length(const ngtcp2_rtb_entry *ent) { + size_t len = 0; + + for (; ent; ent = ent->next) { + ++len; + } + + return len; +} + +void dcid_init(ngtcp2_cid *cid) { + static const uint8_t id[] = "\xff\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xff"; + ngtcp2_cid_init(cid, id, sizeof(id) - 1); +} + +void scid_init(ngtcp2_cid *cid) { + static const uint8_t id[] = "\xee\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xee"; + ngtcp2_cid_init(cid, id, sizeof(id) - 1); +} + +void rcid_init(ngtcp2_cid *cid) { + static const uint8_t id[] = "\xdd\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa\xaa" + "\xaa\xaa\xaa\xaa\xaa\xdd"; + ngtcp2_cid_init(cid, id, sizeof(id) - 1); +} + +uint64_t read_pkt_payloadlen(const uint8_t *pkt, const ngtcp2_cid *dcid, + const ngtcp2_cid *scid) { + uint64_t len; + + ngtcp2_get_uvarint(&len, &pkt[1 + 4 + 1 + dcid->datalen + 1 + scid->datalen]); + + return len; +} + +void write_pkt_payloadlen(uint8_t *pkt, const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, uint64_t payloadlen) { + assert(payloadlen < 1073741824); + ngtcp2_put_uvarint30(&pkt[1 + 4 + 1 + dcid->datalen + 1 + scid->datalen], + (uint32_t)payloadlen); +} + +ngtcp2_ssize pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt, + size_t pktlen) { + const uint8_t *p; + ngtcp2_ssize nread; + + nread = ngtcp2_pkt_decode_hd_long(dest, pkt, pktlen); + if (nread < 0 || (!(dest->flags & NGTCP2_PKT_FLAG_LONG_FORM) && + dest->type == NGTCP2_PKT_VERSION_NEGOTIATION)) { + return nread; + } + + if ((size_t)nread == pktlen) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + p = pkt + nread; + + dest->pkt_numlen = (size_t)(pkt[0] & NGTCP2_PKT_NUMLEN_MASK) + 1; + if (pktlen < (size_t)nread + dest->pkt_numlen) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + dest->pkt_num = ngtcp2_get_pkt_num(p, dest->pkt_numlen); + + return nread + (ngtcp2_ssize)dest->pkt_numlen; +} + +ngtcp2_ssize pkt_decode_hd_short(ngtcp2_pkt_hd *dest, const uint8_t *pkt, + size_t pktlen, size_t dcidlen) { + const uint8_t *p; + ngtcp2_ssize nread; + + nread = ngtcp2_pkt_decode_hd_short(dest, pkt, pktlen, dcidlen); + if (nread < 0) { + return nread; + } + + if ((size_t)nread == pktlen) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + p = pkt + nread; + + dest->pkt_numlen = (size_t)(pkt[0] & NGTCP2_PKT_NUMLEN_MASK) + 1; + if (pktlen < (size_t)nread + dest->pkt_numlen) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + dest->pkt_num = ngtcp2_get_pkt_num(p, dest->pkt_numlen); + + return nread + (ngtcp2_ssize)dest->pkt_numlen; +} + +ngtcp2_ssize pkt_decode_hd_short_mask(ngtcp2_pkt_hd *dest, const uint8_t *pkt, + size_t pktlen, size_t dcidlen) { + static const uint8_t mask[] = NGTCP2_FAKE_HP_MASK; + const uint8_t *p; + ngtcp2_ssize nread; + uint8_t hb; + uint8_t pkt_numbuf[4]; + size_t i; + + nread = ngtcp2_pkt_decode_hd_short(dest, pkt, pktlen, dcidlen); + if (nread < 0) { + return nread; + } + + if ((size_t)nread == pktlen) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + p = pkt + nread; + + hb = (uint8_t)(pkt[0] ^ (mask[0] & 0x1f)); + + dest->pkt_numlen = (size_t)(hb & NGTCP2_PKT_NUMLEN_MASK) + 1; + if (pktlen < (size_t)nread + dest->pkt_numlen) { + return NGTCP2_ERR_INVALID_ARGUMENT; + } + + for (i = 0; i < dest->pkt_numlen; ++i) { + pkt_numbuf[i] = *(p + i) ^ mask[i + 1]; + } + + dest->pkt_num = ngtcp2_get_pkt_num(pkt_numbuf, dest->pkt_numlen); + + return nread + (ngtcp2_ssize)dest->pkt_numlen; +} + +static void addr_init(ngtcp2_sockaddr_in *dest, uint32_t addr, uint16_t port) { + memset(dest, 0, sizeof(*dest)); + + dest->sin_family = AF_INET; + dest->sin_port = ngtcp2_htons(port); + dest->sin_addr.s_addr = ngtcp2_htonl(addr); +} + +void path_init(ngtcp2_path_storage *path, uint32_t local_addr, + uint16_t local_port, uint32_t remote_addr, + uint16_t remote_port) { + ngtcp2_sockaddr_in la, ra; + + addr_init(&la, local_addr, local_port); + addr_init(&ra, remote_addr, remote_port); + + ngtcp2_path_storage_init(path, (ngtcp2_sockaddr *)&la, sizeof(la), + (ngtcp2_sockaddr *)&ra, sizeof(ra), NULL); +} diff --git a/tests/ngtcp2_test_helper.h b/tests/ngtcp2_test_helper.h new file mode 100644 index 0000000..894cdf3 --- /dev/null +++ b/tests/ngtcp2_test_helper.h @@ -0,0 +1,191 @@ +/* + * ngtcp2 + * + * Copyright (c) 2017 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_TEST_HELPER_H +#define NGTCP2_TEST_HELPER_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +#include <ngtcp2/ngtcp2.h> + +#include "ngtcp2_conn.h" + +/* + * strsize macro returns the length of string literal |S|. + */ +#define strsize(S) (sizeof(S) - 1) + +/* + * NGTCP2_APP_ERRxx is an application error code solely used in test + * code. + */ +#define NGTCP2_APP_ERR01 0xff01u +#define NGTCP2_APP_ERR02 0xff02u + +/* + * NGTCP2_FAKE_AEAD_OVERHEAD is AEAD overhead used in unit tests. + * Because we use the same encryption/decryption function for both + * handshake and post handshake packets, we have to use AEAD overhead + * used in handshake packets. + */ +#define NGTCP2_FAKE_AEAD_OVERHEAD NGTCP2_INITIAL_AEAD_OVERHEAD + +/* NGTCP2_FAKE_HP_MASK is a header protection mask used in unit + tests. */ +#define NGTCP2_FAKE_HP_MASK "\x00\x00\x00\x00\x00" + +/* + * ngtcp2_t_encode_stream_frame encodes STREAM frame into |out| with + * the given parameters. If NGTCP2_STREAM_LEN_BIT is set in |flags|, + * |datalen| is encoded as Data Length, otherwise it is not written. + * To set FIN bit in wire format, set NGTCP2_STREAM_FIN_BIT in + * |flags|. This function expects that |out| has enough length to + * store entire STREAM frame, excluding the Stream Data. + * + * This function returns the number of bytes written to |out|. + */ +size_t ngtcp2_t_encode_stream_frame(uint8_t *out, uint8_t flags, + uint64_t stream_id, uint64_t offset, + uint16_t datalen); + +/* + * ngtcp2_t_encode_ack_frame encodes ACK frame into |out| with the + * given parameters. Currently, this function encodes 1 ACK Block + * Section. ACK Delay field is always 0. + * + * This function returns the number of bytes written to |out|. + */ +size_t ngtcp2_t_encode_ack_frame(uint8_t *out, uint64_t largest_ack, + uint64_t first_ack_blklen, uint64_t gap, + uint64_t ack_blklen); + +/* + * write_pkt_flags writes a QUIC packet containing frames pointed by + * |fr| of length |frlen| in |out| whose capacity is |outlen|. This + * function returns the number of bytes written. + */ +size_t write_pkt_flags(uint8_t *out, size_t outlen, uint8_t flags, + const ngtcp2_cid *dcid, int64_t pkt_num, + ngtcp2_frame *fr, size_t frlen, ngtcp2_crypto_km *ckm); + +/* + * write_pkt is write_pkt_flags with flag = NGTCP2_PKT_FLAG_NONE. + */ +size_t write_pkt(uint8_t *out, size_t outlen, const ngtcp2_cid *dcid, + int64_t pkt_num, ngtcp2_frame *fr, size_t frlen, + ngtcp2_crypto_km *ckm); + +/* + * write_handshake_pkt writes a QUIC Initial packet containing |frlen| + * frames pointed by |fr| into |out| whose capacity is |outlen|. This + * function returns the number of bytes written. + */ +size_t write_initial_pkt(uint8_t *out, size_t outlen, const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, int64_t pkt_num, + uint32_t version, const uint8_t *token, + size_t tokenlen, ngtcp2_frame *fr, size_t frlen, + ngtcp2_crypto_km *ckm); + +/* + * write_handshake_pkt writes a QUIC Handshake packet containing + * |frlen| frames pointed by |fr| into |out| whose capacity is + * |outlen|. This function returns the number of bytes written. + */ +size_t write_handshake_pkt(uint8_t *out, size_t outlen, const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, int64_t pkt_num, + uint32_t version, ngtcp2_frame *fr, size_t frlen, + ngtcp2_crypto_km *ckm); + +/* + * write_0rtt_pkt writes a QUIC 0RTT packet containing |frlen| frames + * pointed by |fr| into |out| whose capacity is |outlen|. This + * function returns the number of bytes written. + */ +size_t write_0rtt_pkt(uint8_t *out, size_t outlen, const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, int64_t pkt_num, uint32_t version, + ngtcp2_frame *fr, size_t frlen, ngtcp2_crypto_km *ckm); + +/* + * open_stream opens new stream denoted by |stream_id|. + */ +ngtcp2_strm *open_stream(ngtcp2_conn *conn, int64_t stream_id); + +/* + * rtb_entry_length returns the length of elements pointed by |ent| + * list. + */ +size_t rtb_entry_length(const ngtcp2_rtb_entry *ent); + +void scid_init(ngtcp2_cid *cid); +void dcid_init(ngtcp2_cid *cid); +void rcid_init(ngtcp2_cid *cid); + +/* + * read_pkt_payloadlen reads long header payload length field from + * |pkt|. + */ +uint64_t read_pkt_payloadlen(const uint8_t *pkt, const ngtcp2_cid *dcid, + const ngtcp2_cid *scid); + +/* + * write_pkt_payloadlen writes long header payload length field into + * |pkt|. + */ +void write_pkt_payloadlen(uint8_t *pkt, const ngtcp2_cid *dcid, + const ngtcp2_cid *scid, uint64_t payloadlen); + +/* + * pkt_decode_hd_long decodes long packet header from |pkt| of length + * |pktlen|. This function assumes that header protection has been + * decrypted. + */ +ngtcp2_ssize pkt_decode_hd_long(ngtcp2_pkt_hd *dest, const uint8_t *pkt, + size_t pktlen); + +/* + * pkt_decode_hd_short decodes long packet header from |pkt| of length + * |pktlen|. This function assumes that header protection has been + * decrypted. + */ +ngtcp2_ssize pkt_decode_hd_short(ngtcp2_pkt_hd *dest, const uint8_t *pkt, + size_t pktlen, size_t dcidlen); + +/* + * pkt_decode_hd_short_mask decodes long packet header from |pkt| of + * length |pktlen|. NGTCP2_FAKE_HP_MASK is used to decrypt header + * protection. + */ +ngtcp2_ssize pkt_decode_hd_short_mask(ngtcp2_pkt_hd *dest, const uint8_t *pkt, + size_t pktlen, size_t dcidlen); + +/* + * path_init initializes |path| with the given arguments. They form + * IPv4 addresses. + */ +void path_init(ngtcp2_path_storage *path, uint32_t local_addr, + uint16_t local_port, uint32_t remote_addr, uint16_t remote_port); + +#endif /* NGTCP2_TEST_HELPER_H */ diff --git a/tests/ngtcp2_vec_test.c b/tests/ngtcp2_vec_test.c new file mode 100644 index 0000000..16cfba4 --- /dev/null +++ b/tests/ngtcp2_vec_test.c @@ -0,0 +1,426 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#include "ngtcp2_vec_test.h" + +#include <CUnit/CUnit.h> + +#include "ngtcp2_vec.h" +#include "ngtcp2_test_helper.h" + +void test_ngtcp2_vec_split(void) { + uint8_t nulldata[1024]; + ngtcp2_vec a[16], b[16]; + size_t acnt, bcnt; + ngtcp2_ssize nsplit; + + /* No split occurs */ + acnt = 1; + a[0].len = 135; + a[0].base = nulldata; + + bcnt = 0; + b[0].len = 0; + b[0].base = NULL; + + nsplit = ngtcp2_vec_split(a, &acnt, b, &bcnt, 135, 16); + + CU_ASSERT(0 == nsplit); + CU_ASSERT(1 == acnt); + CU_ASSERT(135 == a[0].len); + CU_ASSERT(nulldata == a[0].base); + CU_ASSERT(0 == bcnt); + CU_ASSERT(0 == b[0].len); + CU_ASSERT(NULL == b[0].base); + + /* Split once */ + acnt = 1; + a[0].len = 135; + a[0].base = nulldata; + + bcnt = 0; + b[0].len = 0; + b[0].base = NULL; + + nsplit = ngtcp2_vec_split(a, &acnt, b, &bcnt, 87, 16); + + CU_ASSERT(48 == nsplit); + CU_ASSERT(1 == acnt); + CU_ASSERT(87 == a[0].len); + CU_ASSERT(nulldata == a[0].base); + CU_ASSERT(1 == bcnt); + CU_ASSERT(48 == b[0].len); + CU_ASSERT(nulldata + 87 == b[0].base); + + /* Multiple a vector; split at ngtcp2_vec boundary */ + acnt = 2; + a[0].len = 33; + a[0].base = nulldata; + a[1].len = 89; + a[1].base = nulldata + 33; + + bcnt = 0; + b[0].len = 0; + b[0].base = NULL; + + nsplit = ngtcp2_vec_split(a, &acnt, b, &bcnt, 33, 16); + + CU_ASSERT(89 == nsplit); + CU_ASSERT(1 == acnt); + CU_ASSERT(33 == a[0].len); + CU_ASSERT(nulldata == a[0].base); + CU_ASSERT(1 == bcnt); + CU_ASSERT(89 == b[0].len); + CU_ASSERT(nulldata + 33 == b[0].base); + + /* Multiple a vector; not split at ngtcp2_vec boundary */ + acnt = 3; + a[0].len = 33; + a[0].base = nulldata; + a[1].len = 89; + a[1].base = nulldata + 33; + a[2].len = 211; + a[2].base = nulldata + 33 + 89; + + bcnt = 0; + b[0].len = 0; + b[0].base = NULL; + + nsplit = ngtcp2_vec_split(a, &acnt, b, &bcnt, 34, 16); + + CU_ASSERT(88 + 211 == nsplit); + CU_ASSERT(2 == acnt); + CU_ASSERT(33 == a[0].len); + CU_ASSERT(nulldata == a[0].base); + CU_ASSERT(1 == a[1].len); + CU_ASSERT(nulldata + 33 == a[1].base); + CU_ASSERT(2 == bcnt); + CU_ASSERT(88 == b[0].len); + CU_ASSERT(nulldata + 34 == b[0].base); + CU_ASSERT(211 == b[1].len); + CU_ASSERT(nulldata + 34 + 88 == b[1].base); + + /* Multiple a vector; split at ngtcp2_vec boundary; continuous + data */ + acnt = 2; + a[0].len = 33; + a[0].base = nulldata; + a[1].len = 89; + a[1].base = nulldata + 33; + + bcnt = 2; + b[0].len = 17; + b[0].base = nulldata + 33 + 89; + b[1].len = 3; + b[1].base = nulldata + 33 + 89 + 17; + + nsplit = ngtcp2_vec_split(a, &acnt, b, &bcnt, 33, 16); + + CU_ASSERT(89 == nsplit); + CU_ASSERT(1 == acnt); + CU_ASSERT(33 == a[0].len); + CU_ASSERT(nulldata == a[0].base); + CU_ASSERT(2 == bcnt); + CU_ASSERT(89 + 17 == b[0].len); + CU_ASSERT(nulldata + 33 == b[0].base); + CU_ASSERT(3 == b[1].len); + CU_ASSERT(nulldata + 33 + 89 + 17 == b[1].base); + + /* Multiple a vector; not split at ngtcp2_vec boundary; continuous + data; nmove == 0 */ + acnt = 2; + a[0].len = 33; + a[0].base = nulldata; + a[1].len = 89; + a[1].base = nulldata + 33; + + bcnt = 2; + b[0].len = 17; + b[0].base = nulldata + 33 + 89; + b[1].len = 3; + b[1].base = nulldata + 33 + 89 + 17; + + nsplit = ngtcp2_vec_split(a, &acnt, b, &bcnt, 34, 16); + + CU_ASSERT(88 == nsplit); + CU_ASSERT(2 == acnt); + CU_ASSERT(33 == a[0].len); + CU_ASSERT(nulldata == a[0].base); + CU_ASSERT(1 == a[1].len); + CU_ASSERT(nulldata + 33 == a[1].base); + CU_ASSERT(2 == bcnt); + CU_ASSERT(88 + 17 == b[0].len); + CU_ASSERT(nulldata + 34 == b[0].base); + CU_ASSERT(3 == b[1].len); + CU_ASSERT(nulldata + 33 + 89 + 17 == b[1].base); + + /* Multiple a vector; not split at ngtcp2_vec boundary; continuous + data */ + acnt = 3; + a[0].len = 33; + a[0].base = nulldata; + a[1].len = 89; + a[1].base = nulldata + 33; + a[2].len = 211; + a[2].base = nulldata + 33 + 89; + + bcnt = 2; + b[0].len = 17; + b[0].base = nulldata + 33 + 89 + 211; + b[1].len = 3; + b[1].base = nulldata + 33 + 89 + 211 + 17; + + nsplit = ngtcp2_vec_split(a, &acnt, b, &bcnt, 34, 16); + + CU_ASSERT(88 + 211 == nsplit); + CU_ASSERT(2 == acnt); + CU_ASSERT(33 == a[0].len); + CU_ASSERT(nulldata == a[0].base); + CU_ASSERT(1 == a[1].len); + CU_ASSERT(nulldata + 33 == a[1].base); + CU_ASSERT(3 == bcnt); + CU_ASSERT(88 == b[0].len); + CU_ASSERT(nulldata + 34 == b[0].base); + CU_ASSERT(211 + 17 == b[1].len); + CU_ASSERT(nulldata + 34 + 88 == b[1].base); + CU_ASSERT(3 == b[2].len); + CU_ASSERT(nulldata + 33 + 89 + 211 + 17 == b[2].base); + + /* Multiple a vector; split at ngtcp2_vec boundary; not continuous + data */ + acnt = 2; + a[0].len = 33; + a[0].base = nulldata; + a[1].len = 89; + a[1].base = nulldata + 33; + + bcnt = 2; + b[0].len = 17; + b[0].base = nulldata + 256; + b[1].len = 3; + b[1].base = nulldata + 256 + 17; + + nsplit = ngtcp2_vec_split(a, &acnt, b, &bcnt, 33, 16); + + CU_ASSERT(89 == nsplit); + CU_ASSERT(1 == acnt); + CU_ASSERT(33 == a[0].len); + CU_ASSERT(nulldata == a[0].base); + CU_ASSERT(3 == bcnt); + CU_ASSERT(89 == b[0].len); + CU_ASSERT(nulldata + 33 == b[0].base); + CU_ASSERT(17 == b[1].len); + CU_ASSERT(nulldata + 256 == b[1].base); + CU_ASSERT(3 == b[2].len); + CU_ASSERT(nulldata + 256 + 17 == b[2].base); + + /* maxcnt exceeded; continuous */ + acnt = 2; + a[0].len = 33; + a[0].base = nulldata; + a[1].len = 89; + a[1].base = nulldata + 33; + + bcnt = 1; + b[0].len = 17; + b[0].base = nulldata + 33 + 89; + + nsplit = ngtcp2_vec_split(a, &acnt, b, &bcnt, 32, 1); + + CU_ASSERT(-1 == nsplit); + + /* maxcnt exceeded; not continuous */ + acnt = 2; + a[0].len = 33; + a[0].base = nulldata; + a[1].len = 89; + a[1].base = nulldata + 33; + + bcnt = 1; + b[0].len = 17; + b[0].base = nulldata + 256; + + nsplit = ngtcp2_vec_split(a, &acnt, b, &bcnt, 33, 1); + + CU_ASSERT(-1 == nsplit); +} + +void test_ngtcp2_vec_merge(void) { + uint8_t nulldata[1024]; + ngtcp2_vec a[16], b[16]; + size_t acnt, bcnt; + size_t nmerged; + + /* Merge one ngtcp2_vec completely */ + acnt = 1; + a[0].len = 33; + a[0].base = nulldata; + + bcnt = 1; + b[0].len = 11; + b[0].base = nulldata + 33; + + nmerged = ngtcp2_vec_merge(a, &acnt, b, &bcnt, 11, 16); + + CU_ASSERT(11 == nmerged); + CU_ASSERT(1 == acnt); + CU_ASSERT(44 == a[0].len); + CU_ASSERT(nulldata == a[0].base); + CU_ASSERT(0 == bcnt); + + /* Merge ngtcp2_vec partially */ + acnt = 1; + a[0].len = 33; + a[0].base = nulldata; + + bcnt = 1; + b[0].len = 11; + b[0].base = nulldata + 33; + + nmerged = ngtcp2_vec_merge(a, &acnt, b, &bcnt, 10, 16); + + CU_ASSERT(10 == nmerged); + CU_ASSERT(1 == acnt); + CU_ASSERT(43 == a[0].len); + CU_ASSERT(nulldata == a[0].base); + CU_ASSERT(1 == bcnt); + CU_ASSERT(1 == b[0].len); + CU_ASSERT(nulldata + 33 + 10 == b[0].base); + + /* Merge one ngtcp2_vec completely; data is not continuous */ + acnt = 1; + a[0].len = 33; + a[0].base = nulldata; + + bcnt = 1; + b[0].len = 11; + b[0].base = nulldata + 256; + + nmerged = ngtcp2_vec_merge(a, &acnt, b, &bcnt, 11, 16); + + CU_ASSERT(11 == nmerged); + CU_ASSERT(2 == acnt); + CU_ASSERT(33 == a[0].len); + CU_ASSERT(nulldata == a[0].base); + CU_ASSERT(11 == a[1].len); + CU_ASSERT(nulldata + 256 == a[1].base); + CU_ASSERT(0 == bcnt); + + /* Merge ngtcp2_vec partially; data is not continuous */ + acnt = 1; + a[0].len = 33; + a[0].base = nulldata; + + bcnt = 1; + b[0].len = 11; + b[0].base = nulldata + 256; + + nmerged = ngtcp2_vec_merge(a, &acnt, b, &bcnt, 10, 16); + + CU_ASSERT(10 == nmerged); + CU_ASSERT(2 == acnt); + CU_ASSERT(33 == a[0].len); + CU_ASSERT(nulldata == a[0].base); + CU_ASSERT(10 == a[1].len); + CU_ASSERT(nulldata + 256 == a[1].base); + CU_ASSERT(1 == bcnt); + CU_ASSERT(1 == b[0].len); + CU_ASSERT(nulldata + 256 + 10 == b[0].base); + + /* Merge ends at the ngtcp2_vec boundary */ + acnt = 1; + a[0].len = 33; + a[0].base = nulldata; + + bcnt = 2; + b[0].len = 11; + b[0].base = nulldata + 256; + b[1].len = 19; + b[1].base = nulldata + 256 + 11; + + nmerged = ngtcp2_vec_merge(a, &acnt, b, &bcnt, 11, 16); + + CU_ASSERT(11 == nmerged); + CU_ASSERT(2 == acnt); + CU_ASSERT(33 == a[0].len); + CU_ASSERT(nulldata == a[0].base); + CU_ASSERT(11 == a[1].len); + CU_ASSERT(nulldata + 256 == a[1].base); + CU_ASSERT(1 == bcnt); + CU_ASSERT(19 == b[0].len); + CU_ASSERT(nulldata + 256 + 11 == b[0].base); + + /* Merge occurs at the last object */ + acnt = 1; + a[0].len = 33; + a[0].base = nulldata; + + bcnt = 2; + b[0].len = 11; + b[0].base = nulldata + 33; + b[1].len = 99; + b[1].base = nulldata + 33 + 11; + + nmerged = ngtcp2_vec_merge(a, &acnt, b, &bcnt, 100, 1); + + CU_ASSERT(100 == nmerged); + CU_ASSERT(1 == acnt); + CU_ASSERT(133 == a[0].len); + CU_ASSERT(nulldata == a[0].base); + CU_ASSERT(1 == bcnt); + CU_ASSERT(10 == b[0].len); + CU_ASSERT(nulldata + 33 + 11 + 89 == b[0].base); + + /* No merge occurs if object is full */ + acnt = 1; + a[0].len = 33; + a[0].base = nulldata; + + bcnt = 1; + b[0].len = 3; + b[0].base = nulldata + 100; + + nmerged = ngtcp2_vec_merge(a, &acnt, b, &bcnt, 3, 1); + + CU_ASSERT(0 == nmerged); +} + +void test_ngtcp2_vec_len_varint(void) { + CU_ASSERT(0 == ngtcp2_vec_len_varint(NULL, 0)); + +#if SIZE_MAX == UINT64_MAX + { + ngtcp2_vec v[] = {{NULL, NGTCP2_MAX_VARINT}, {NULL, 1}}; + + CU_ASSERT(-1 == ngtcp2_vec_len_varint(v, ngtcp2_arraylen(v))); + } + + { + ngtcp2_vec v[] = {{NULL, NGTCP2_MAX_VARINT - 1}, {NULL, 1}}; + + CU_ASSERT(NGTCP2_MAX_VARINT == + ngtcp2_vec_len_varint(v, ngtcp2_arraylen(v))); + } +#endif /* SIZE_MAX == UINT64_MAX */ +} diff --git a/tests/ngtcp2_vec_test.h b/tests/ngtcp2_vec_test.h new file mode 100644 index 0000000..56e5d54 --- /dev/null +++ b/tests/ngtcp2_vec_test.h @@ -0,0 +1,36 @@ +/* + * ngtcp2 + * + * Copyright (c) 2018 ngtcp2 contributors + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +#ifndef NGTCP2_VEC_TEST_H +#define NGTCP2_VEC_TEST_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif /* HAVE_CONFIG_H */ + +void test_ngtcp2_vec_split(void); +void test_ngtcp2_vec_merge(void); +void test_ngtcp2_vec_len_varint(void); + +#endif /* NGTCP2_VEC_TEST_H */ |