diff --git a/lib/ngtcp2_conn.c b/lib/ngtcp2_conn.c index 909648079c074174db365dfe461fd74e290731b4..afc9d71e19cf0a5398419433c524aeb80da074c2 100644 --- a/lib/ngtcp2_conn.c +++ b/lib/ngtcp2_conn.c @@ -8964,36 +8964,15 @@ fin: return nwrite; } -ngtcp2_ssize ngtcp2_conn_write_connection_close(ngtcp2_conn *conn, - ngtcp2_path *path, +static ngtcp2_ssize conn_write_connection_close(ngtcp2_conn *conn, uint8_t *dest, size_t destlen, + uint8_t pkt_type, uint64_t error_code, ngtcp2_tstamp ts) { ngtcp2_pktns *in_pktns = conn->in_pktns; ngtcp2_pktns *hs_pktns = conn->hs_pktns; ngtcp2_ssize res = 0, nwrite; ngtcp2_frame fr; - uint8_t pkt_type; - - conn->log.last_ts = ts; - conn->qlog.last_ts = ts; - - if (conn_check_pkt_num_exhausted(conn)) { - return NGTCP2_ERR_PKT_NUM_EXHAUSTED; - } - - switch (conn->state) { - case NGTCP2_CS_CLIENT_INITIAL: - case NGTCP2_CS_CLOSING: - case NGTCP2_CS_DRAINING: - return NGTCP2_ERR_INVALID_STATE; - default: - break; - } - - if (path) { - ngtcp2_path_copy(path, &conn->dcid.current.ps.path); - } fr.type = NGTCP2_FRAME_CONNECTION_CLOSE; fr.connection_close.error_code = error_code; @@ -9001,18 +8980,6 @@ ngtcp2_ssize ngtcp2_conn_write_connection_close(ngtcp2_conn *conn, fr.connection_close.reasonlen = 0; fr.connection_close.reason = NULL; - if (conn->state == NGTCP2_CS_POST_HANDSHAKE) { - pkt_type = NGTCP2_PKT_SHORT; - } else if (hs_pktns && hs_pktns->crypto.tx.ckm) { - pkt_type = NGTCP2_PKT_HANDSHAKE; - } else if (in_pktns && in_pktns->crypto.tx.ckm) { - pkt_type = NGTCP2_PKT_INITIAL; - } else { - /* This branch is taken if server has not read any Initial packet - from client. */ - return NGTCP2_ERR_INVALID_STATE; - } - if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED) && pkt_type != NGTCP2_PKT_INITIAL) { if (in_pktns && conn->server) { @@ -9057,9 +9024,61 @@ ngtcp2_ssize ngtcp2_conn_write_connection_close(ngtcp2_conn *conn, return NGTCP2_ERR_NOBUF; } + return res; +} + +ngtcp2_ssize ngtcp2_conn_write_connection_close(ngtcp2_conn *conn, + ngtcp2_path *path, + uint8_t *dest, size_t destlen, + uint64_t error_code, + ngtcp2_tstamp ts) { + ngtcp2_pktns *in_pktns = conn->in_pktns; + ngtcp2_pktns *hs_pktns = conn->hs_pktns; + uint8_t pkt_type; + ngtcp2_ssize nwrite; + + conn->log.last_ts = ts; + conn->qlog.last_ts = ts; + + if (conn_check_pkt_num_exhausted(conn)) { + return NGTCP2_ERR_PKT_NUM_EXHAUSTED; + } + + switch (conn->state) { + case NGTCP2_CS_CLIENT_INITIAL: + case NGTCP2_CS_CLOSING: + case NGTCP2_CS_DRAINING: + return NGTCP2_ERR_INVALID_STATE; + default: + break; + } + + if (path) { + ngtcp2_path_copy(path, &conn->dcid.current.ps.path); + } + + if (conn->state == NGTCP2_CS_POST_HANDSHAKE || + (conn->server && conn->pktns.crypto.tx.ckm)) { + pkt_type = NGTCP2_PKT_SHORT; + } else if (hs_pktns && hs_pktns->crypto.tx.ckm) { + pkt_type = NGTCP2_PKT_HANDSHAKE; + } else if (in_pktns && in_pktns->crypto.tx.ckm) { + pkt_type = NGTCP2_PKT_INITIAL; + } else { + /* This branch is taken if server has not read any Initial packet + from client. */ + return NGTCP2_ERR_INVALID_STATE; + } + + nwrite = conn_write_connection_close(conn, dest, destlen, pkt_type, + error_code, ts); + if (nwrite < 0) { + return nwrite; + } + conn->state = NGTCP2_CS_CLOSING; - return res; + return nwrite; } ngtcp2_ssize ngtcp2_conn_write_application_close(ngtcp2_conn *conn, @@ -9068,13 +9087,9 @@ ngtcp2_ssize ngtcp2_conn_write_application_close(ngtcp2_conn *conn, uint64_t app_error_code, ngtcp2_tstamp ts) { ngtcp2_ssize nwrite; + ngtcp2_ssize res = 0; ngtcp2_frame fr; - if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)) { - return ngtcp2_conn_write_connection_close(conn, path, dest, destlen, - NGTCP2_APPLICATION_ERROR, ts); - } - conn->log.last_ts = ts; conn->qlog.last_ts = ts; @@ -9083,12 +9098,38 @@ ngtcp2_ssize ngtcp2_conn_write_application_close(ngtcp2_conn *conn, } switch (conn->state) { - case NGTCP2_CS_POST_HANDSHAKE: - break; - default: + case NGTCP2_CS_CLIENT_INITIAL: + case NGTCP2_CS_CLOSING: + case NGTCP2_CS_DRAINING: return NGTCP2_ERR_INVALID_STATE; + default: + break; + } + + if (!(conn->flags & NGTCP2_CONN_FLAG_HANDSHAKE_CONFIRMED)) { + nwrite = conn_write_connection_close(conn, dest, destlen, + conn->hs_pktns->crypto.tx.ckm + ? NGTCP2_PKT_HANDSHAKE + : NGTCP2_PKT_INITIAL, + NGTCP2_APPLICATION_ERROR, ts); + if (nwrite < 0) { + return nwrite; + } + res = nwrite; + dest += nwrite; + destlen -= (size_t)nwrite; + } + + if (conn->state != NGTCP2_CS_POST_HANDSHAKE) { + assert(res); + + if (!conn->server || !conn->pktns.crypto.tx.ckm) { + return res; + } } + assert(conn->pktns.crypto.tx.ckm); + if (path) { ngtcp2_path_copy(path, &conn->dcid.current.ps.path); } @@ -9107,13 +9148,15 @@ ngtcp2_ssize ngtcp2_conn_write_application_close(ngtcp2_conn *conn, return nwrite; } - if (nwrite == 0) { + res += (size_t)nwrite; + + if (res == 0) { return NGTCP2_ERR_NOBUF; } conn->state = NGTCP2_CS_CLOSING; - return nwrite; + return res; } int ngtcp2_conn_is_in_closing_period(ngtcp2_conn *conn) { diff --git a/tests/main.c b/tests/main.c index 5290f638be871926b5b9b7b705ecc139be05e456..b6c46b8da8c3b14c77c938c4e03058ca549be99a 100644 --- a/tests/main.c +++ b/tests/main.c @@ -260,6 +260,8 @@ int main() { 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, "pkt_write_connection_close", diff --git a/tests/ngtcp2_conn_test.c b/tests/ngtcp2_conn_test.c index d1b313d1ec9f067a78d361327192e36a5548ecfb..33f50b34346cc599d6e4ce3a96571f3929891359 100644 --- a/tests/ngtcp2_conn_test.c +++ b/tests/ngtcp2_conn_test.c @@ -5629,6 +5629,189 @@ void test_ngtcp2_conn_write_connection_close(void) { 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; + + /* Client only Initial key */ + setup_handshake_client(&conn); + + spktlen = ngtcp2_conn_write_pkt(conn, NULL, buf, sizeof(buf), 0); + + CU_ASSERT(spktlen > 0); + + spktlen = ngtcp2_conn_write_application_close(conn, NULL, buf, sizeof(buf), + app_err_code, 0); + + CU_ASSERT(spktlen > 0); + + 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, buf, sizeof(buf), 0); + + CU_ASSERT(spktlen > 0); + + ngtcp2_conn_install_tx_handshake_key(conn, &aead_ctx, null_iv, + sizeof(null_iv), &hp_ctx); + ngtcp2_conn_set_aead_overhead(conn, NGTCP2_FAKE_AEAD_OVERHEAD); + + spktlen = ngtcp2_conn_write_application_close(conn, NULL, buf, sizeof(buf), + app_err_code, 0); + + CU_ASSERT(spktlen > 0); + + 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); + + 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_conn_set_aead_overhead(conn, NGTCP2_FAKE_AEAD_OVERHEAD); + + conn->state = NGTCP2_CS_POST_HANDSHAKE; + + spktlen = ngtcp2_conn_write_application_close(conn, NULL, buf, sizeof(buf), + app_err_code, 0); + + CU_ASSERT(spktlen > 0); + + 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_SHORT == hd.type); + + ngtcp2_conn_del(conn); + + /* Client has confirmed handshake */ + setup_default_client(&conn); + + spktlen = ngtcp2_conn_write_application_close(conn, NULL, buf, sizeof(buf), + app_err_code, 0); + + CU_ASSERT(spktlen > 0); + + shdlen = ngtcp2_pkt_decode_hd_short(&hd, buf, (size_t)spktlen, + conn->dcid.current.cid.datalen); + + CU_ASSERT(shdlen > 0); + CU_ASSERT(NGTCP2_PKT_SHORT == hd.type); + + ngtcp2_conn_del(conn); + + /* Server has Initial and Handshake key */ + setup_handshake_server(&conn); + + spktlen = ngtcp2_conn_write_application_close(conn, NULL, buf, sizeof(buf), + app_err_code, 0); + + CU_ASSERT(spktlen > 0); + + 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); + + ngtcp2_conn_install_tx_key(conn, null_secret, sizeof(null_secret), &aead_ctx, + null_iv, sizeof(null_iv), &hp_ctx); + + spktlen = ngtcp2_conn_write_application_close(conn, NULL, buf, sizeof(buf), + app_err_code, 0); + + CU_ASSERT(spktlen > 0); + + 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_SHORT == hd.type); + + ngtcp2_conn_del(conn); + + /* Server has confirmed handshake */ + setup_default_server(&conn); + + spktlen = ngtcp2_conn_write_application_close(conn, NULL, buf, sizeof(buf), + app_err_code, 0); + + CU_ASSERT(spktlen > 0); + + shdlen = ngtcp2_pkt_decode_hd_short(&hd, buf, (size_t)spktlen, + conn->dcid.current.cid.datalen); + + CU_ASSERT(shdlen > 0); + CU_ASSERT(NGTCP2_PKT_SHORT == hd.type); + + ngtcp2_conn_del(conn); +} + void test_ngtcp2_conn_rtb_reclaim_on_pto(void) { ngtcp2_conn *conn; int rv; diff --git a/tests/ngtcp2_conn_test.h b/tests/ngtcp2_conn_test.h index 8c8f6d7fda5494216d6449598625c2f70d99d316..5be0d3acac0a52f03401d14bae6677ab4855b6d2 100644 --- a/tests/ngtcp2_conn_test.h +++ b/tests/ngtcp2_conn_test.h @@ -75,6 +75,7 @@ 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_pkt_write_connection_close(void);