[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[gnunet] 20/39: http3: Handle errors and refactor the code for receiving
From: |
gnunet |
Subject: |
[gnunet] 20/39: http3: Handle errors and refactor the code for receiving packets |
Date: |
Sun, 25 Aug 2024 15:16:16 +0200 |
This is an automated email from the git hooks/post-receive script.
martin-schanzenbach pushed a commit to branch master
in repository gnunet.
commit 5d4a3b3aee732e8b3567775bc0f3fe3766eeb91e
Author: Shichao <mrrr61@outlook.com>
AuthorDate: Thu Jun 13 12:33:58 2024 +0800
http3: Handle errors and refactor the code for receiving packets
---
src/service/transport/gnunet-communicator-http3.c | 519 +++++++++++++++++++++-
1 file changed, 505 insertions(+), 14 deletions(-)
diff --git a/src/service/transport/gnunet-communicator-http3.c
b/src/service/transport/gnunet-communicator-http3.c
index 2def0fad7..e85fd9f32 100644
--- a/src/service/transport/gnunet-communicator-http3.c
+++ b/src/service/transport/gnunet-communicator-http3.c
@@ -46,6 +46,19 @@
*/
#define ADDRESS_VALIDITY_PERIOD GNUNET_TIME_UNIT_HOURS
+/**
+ * Defines some error types related to network errors.
+ */
+enum network_error
+{
+ NETWORK_ERR_OK = 0,
+ NETWORK_ERR_FATAL = -10,
+ NETWORK_ERR_SEND_BLOCKED = -11,
+ NETWORK_ERR_CLOSE_WAIT = -12,
+ NETWORK_ERR_RETRY = -13,
+ NETWORK_ERR_DROP_CONN = -14,
+};
+
/**
* Map of sockaddr -> struct Connection
*
@@ -1370,7 +1383,7 @@ close_waitcb (void *cls)
*
* @param connection The connection
*/
-static void
+static void
start_draining_period (struct Connection *connection)
{
ngtcp2_duration pto;
@@ -1394,7 +1407,7 @@ start_draining_period (struct Connection *connection)
/**
* Start the closing period and build the packet contains CONNECTION_CLOSE.
- * If we are server side, the function will set the #close_waitcb and write
+ * If we are server side, the function will set the #close_waitcb and write
* the packet to the conn_closebuf.
* If we are client side, send the CONNECTION_CLOSE packet directly, and won't
* wait close.
@@ -1473,7 +1486,7 @@ start_closing_period (struct Connection *connection)
* Send the packet in the conn_closebuf.
*
* @param connection the connection
- * @return #GNUNET_NO if success, #GNUNET_SYSERR if failed.
+ * @return #GNUNET_NO if success, #GNUNET_SYSERR if failed.
*/
static int
send_conn_close (struct Connection *connection)
@@ -1489,6 +1502,76 @@ send_conn_close (struct Connection *connection)
}
+/**
+ * Handle errors. Both server and client will use this function.
+ *
+ * @param connection the connection.
+ * @return #GNUNET_NO if success, #GNUNET_SYSERR if failed or we are client
side,
+ * #NETWORK_ERR_CLOSE_WAIT if need to wait for close.
+ */
+static int
+handle_error (struct Connection *connection)
+{
+ int rv;
+
+ /* if we are the client side */
+ if (GNUNET_YES == connection->is_initiator)
+ {
+ /* this will send CONNECTION_CLOSE immedietly and don't wait */
+ start_closing_period (connection);
+ connection_destroy (connection);
+ return GNUNET_SYSERR;
+ }
+
+ if (NGTCP2_CCERR_TYPE_IDLE_CLOSE == connection->last_error.type)
+ {
+ return GNUNET_SYSERR;
+ }
+
+ if (GNUNET_NO != start_closing_period (connection))
+ {
+ return GNUNET_SYSERR;
+ }
+
+ if (ngtcp2_conn_in_draining_period (connection->conn))
+ {
+ return NETWORK_ERR_CLOSE_WAIT;
+ }
+
+ rv = send_conn_close (connection);
+ if (NETWORK_ERR_OK != rv)
+ {
+ return rv;
+ }
+
+ return NETWORK_ERR_CLOSE_WAIT;
+}
+
+
+/**
+ * Handles expired timer.
+ *
+ * @param connection the connection
+ * @return #GNUNET_NO if success, else return #handle_error.
+ */
+static int
+handle_expiry (struct Connection *connection)
+{
+ int rv;
+
+ rv = ngtcp2_conn_handle_expiry (connection->conn, timestamp ());
+ if (0 != rv)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "ngtcp2_conn_handle_expiry: %s\n",
+ ngtcp2_strerror (rv));
+ ngtcp2_ccerr_set_liberr (&connection->last_error, rv, NULL, 0);
+ return handle_error (connection);
+ }
+ return GNUNET_NO;
+}
+
+
/**
* The timer callback function.
*
@@ -1503,21 +1586,39 @@ timeoutcb (void *cls)
connection->timer = NULL;
int rv;
- rv = ngtcp2_conn_handle_expiry (connection->conn, timestamp ());
- if (0 != rv)
+ rv = handle_expiry (connection);
+ if (GNUNET_NO != rv)
{
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "ngtcp2_conn_handle_expiry: %s\n",
- ngtcp2_strerror (rv));
- connection_destroy (connection);
- return;
+ if (GNUNET_YES == connection->is_initiator)
+ {
+ return;
+ }
+ switch (rv)
+ {
+ case NETWORK_ERR_CLOSE_WAIT:
+ return;
+ default:
+ connection_destroy (connection);
+ return;
+ }
}
+
rv = connection_write (connection);
- if (0 != rv)
+ if (GNUNET_YES == connection->is_initiator)
{
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "connection_write error!\n");
- connection_destroy (connection);
+ return;
+ }
+ if (GNUNET_NO != rv)
+ {
+ switch (rv)
+ {
+ case NETWORK_ERR_CLOSE_WAIT:
+ return;
+ default:
+ connection_destroy (connection);
+ return;
+ }
+
}
}
@@ -1998,6 +2099,365 @@ accept_connection (struct sockaddr *local_addr,
}
+/**
+ * Decrypt QUIC packet. Both the server and the client will use this function.
+ *
+ * @param connection the connection
+ * @param local_addr our address
+ * @param local_addrlen length of our address
+ * @param remote_addr peer's address
+ * @param remote_addrlen length of peer's address
+ * @param pi ngtcp2 packet info
+ * @param data the QUIC packet to be processed
+ * @param datalen the length of data
+ *
+ * @return #GNUNET_NO if success, or a negative value.
+ */
+static int
+connection_feed_data (struct Connection *connection,
+ struct sockaddr *local_addr, socklen_t local_addrlen,
+ struct sockaddr *remote_addr, socklen_t remote_addrlen,
+ const ngtcp2_pkt_info *pi,
+ const uint8_t *data, size_t datalen)
+{
+ ngtcp2_path path;
+ int rv;
+
+ path.local.addr = local_addr;
+ path.local.addrlen = local_addrlen;
+ path.remote.addr = remote_addr;
+ path.remote.addrlen = remote_addrlen;
+
+ rv = ngtcp2_conn_read_pkt (connection->conn, &path, pi, data, datalen,
+ timestamp ());
+ if (0 != rv)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "ngtcp2_conn_read_pkt: %s\n",
+ ngtcp2_strerror (rv));
+ switch (rv)
+ {
+ case NGTCP2_ERR_DRAINING:
+ if (GNUNET_NO == connection->is_initiator)
+ {
+ start_draining_period (connection);
+ return NETWORK_ERR_CLOSE_WAIT;
+ }
+ else
+ {
+ ngtcp2_ccerr_set_liberr (&connection->last_error, rv, NULL, 0);
+ }
+ case NGTCP2_ERR_RETRY:
+ /* client side doesn't get this */
+ return NETWORK_ERR_RETRY;
+ case NGTCP2_ERR_DROP_CONN:
+ /* client side doesn't get this */
+ return NETWORK_ERR_DROP_CONN;
+ case NGTCP2_ERR_CRYPTO:
+ if (! connection->last_error.error_code)
+ {
+ ngtcp2_ccerr_set_tls_alert (
+ &connection->last_error,
+ ngtcp2_conn_get_tls_alert (connection->conn),
+ NULL, 0);
+ }
+ break;
+ default:
+ if (! connection->last_error.error_code)
+ {
+ ngtcp2_ccerr_set_liberr (&connection->last_error, rv, NULL, 0);
+ }
+ }
+ return handle_error (connection);
+ }
+ return GNUNET_NO;
+}
+
+
+/**
+ * Connection read the packet data. This function will only be called by the
server.
+ *
+ * @param connection the connection
+ * @param local_addr our address
+ * @param local_addrlen length of our address
+ * @param remote_addr peer's address
+ * @param remote_addrlen length of peer's address
+ * @param pi ngtcp2 packet info
+ * @param data the QUIC packet to be processed
+ * @param datalen the length of data
+ *
+ * @return #GNUNET_NO if success, or the return
+ * value of #connection_feed_data.
+ */
+static int
+connection_on_read (struct Connection *connection,
+ struct sockaddr *local_addr, socklen_t local_addrlen,
+ struct sockaddr *remote_addr, socklen_t remote_addrlen,
+ const ngtcp2_pkt_info *pi,
+ const uint8_t *data, size_t datalen)
+{
+ int rv;
+ rv = connection_feed_data (connection, local_addr, local_addrlen,
remote_addr,
+ remote_addrlen, pi, data, datalen);
+ if (GNUNET_NO != rv)
+ {
+ return rv;
+ }
+
+ connection_update_timer (connection);
+ return GNUNET_NO;
+}
+
+
+/**
+ * Create a new connection. This function will only be called by the server.
+ *
+ * @param local_addr our address
+ * @param local_addrlen length of our address
+ * @param remote_addr peer's address
+ * @param remote_addrlen length of peer's address
+ * @param dcid scid of the data packet from the client
+ * @param scid dcid of the data packet from the client
+ * @param version version of the data packet from the client
+ *
+ * @return a new connection, NULL if error occurs.
+ */
+struct Connection *
+connection_init (struct sockaddr *local_addr,
+ socklen_t local_addrlen,
+ struct sockaddr *remote_addr,
+ socklen_t remote_addrlen,
+ const ngtcp2_cid *dcid, const ngtcp2_cid *scid,
+ uint32_t version)
+{
+ struct Connection *new_connection;
+ ngtcp2_path path;
+ ngtcp2_transport_params params;
+ ngtcp2_cid scid_;
+ ngtcp2_conn *conn = NULL;
+ ngtcp2_settings settings;
+ ngtcp2_callbacks callbacks = {
+ // .client_initial
+ .recv_client_initial = ngtcp2_crypto_recv_client_initial_cb,
+ .recv_crypto_data = ngtcp2_crypto_recv_crypto_data_cb,
+ .encrypt = ngtcp2_crypto_encrypt_cb,
+ .decrypt = ngtcp2_crypto_decrypt_cb,
+ .hp_mask = ngtcp2_crypto_hp_mask_cb,
+ // .recv_retry = ngtcp2_crypto_recv_retry_cb,
+ .update_key = ngtcp2_crypto_update_key_cb,
+ .delete_crypto_aead_ctx = ngtcp2_crypto_delete_crypto_aead_ctx_cb,
+ .delete_crypto_cipher_ctx = ngtcp2_crypto_delete_crypto_cipher_ctx_cb,
+ .get_path_challenge_data = ngtcp2_crypto_get_path_challenge_data_cb,
+ .version_negotiation = ngtcp2_crypto_version_negotiation_cb,
+
+ // .acked_stream_data_offset = acked_stream_data_offset_cb,
+ .recv_stream_data = recv_stream_data_cb,
+ // .stream_open = stream_open_cb,
+ .rand = rand_cb,
+ .get_new_connection_id = get_new_connection_id_cb,
+ .stream_close = stream_close_cb,
+ };
+
+
+ int rv;
+
+ path.local.addr = local_addr;
+ path.local.addrlen = local_addrlen;
+ path.remote.addr = remote_addr;
+ path.remote.addrlen = remote_addrlen;
+
+ new_connection = GNUNET_new (struct Connection);
+ memset (new_connection, 0, sizeof (struct Connection));
+
+ gnutls_init (&new_connection->session,
+ GNUTLS_SERVER
+ | GNUTLS_ENABLE_EARLY_DATA
+ | GNUTLS_NO_END_OF_EARLY_DATA);
+ gnutls_priority_set_direct (new_connection->session, PRIORITY, NULL);
+ gnutls_credentials_set (new_connection->session,
+ GNUTLS_CRD_CERTIFICATE, cred);
+
+ ngtcp2_transport_params_default (¶ms);
+ params.initial_max_streams_uni = 3;
+ params.initial_max_streams_bidi = 128;
+ params.initial_max_stream_data_bidi_local = 128 * 1024;
+ params.initial_max_stream_data_bidi_remote = 128 * 1024;
+ params.initial_max_data = 1024 * 1024;
+ params.original_dcid_present = 1;
+ params.max_idle_timeout = 30 * NGTCP2_SECONDS;
+ params.original_dcid = *scid;
+
+ ngtcp2_settings_default (&settings);
+
+ scid_.datalen = NGTCP2_MAX_CIDLEN;
+ GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_STRONG,
+ &scid_.data, scid_.datalen);
+
+ rv = ngtcp2_conn_server_new (&conn,
+ dcid,
+ &scid_,
+ &path,
+ version,
+ &callbacks,
+ &settings,
+ ¶ms,
+ NULL,
+ new_connection);
+ if (rv < 0)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "ngtcp2_conn_server_new: %s\n",
+ ngtcp2_strerror (rv));
+ return NULL;
+ }
+ new_connection->conn = conn;
+ new_connection->address = GNUNET_memdup (remote_addr, remote_addrlen);
+ new_connection->address_len = remote_addrlen;
+ new_connection->foreign_addr =
+ sockaddr_to_udpaddr_string (new_connection->address,
+ new_connection->address_len);
+ new_connection->is_initiator = GNUNET_NO;
+ new_connection->id_rcvd = GNUNET_NO;
+ new_connection->id_sent = GNUNET_NO;
+ ngtcp2_crypto_gnutls_configure_server_session (new_connection->session);
+ ngtcp2_conn_set_tls_native_handle (new_connection->conn,
+ new_connection->session);
+ gnutls_session_set_ptr (new_connection->session,
+ &new_connection->conn_ref);
+
+ new_connection->conn_ref.get_conn = get_conn;
+ new_connection->conn_ref.user_data = new_connection;
+ new_connection->streams = GNUNET_CONTAINER_multihashmap_create (10,
+ GNUNET_NO);
+
+ return new_connection;
+}
+
+
+/**
+ * The server processes the newly received data packet.
+ * This function will only be called by the server.
+ *
+ * @param connection the connection
+ * @param addr_key the hash key of peer's address
+ * @param local_addr our address
+ * @param local_addrlen length of our address
+ * @param remote_addr peer's address
+ * @param remote_addrlen length of peer's address
+ * @param pi ngtcp2 packet info
+ * @param data the QUIC packet to be processed
+ * @param datalen the length of data
+ */
+static void
+server_read_pkt (struct Connection *connection,
+ const struct GNUNET_HashCode *addr_key,
+ struct sockaddr *local_addr, socklen_t local_addrlen,
+ struct sockaddr *remote_addr, socklen_t remote_addrlen,
+ const ngtcp2_pkt_info *pi,
+ const uint8_t *data, size_t datalen)
+{
+ ngtcp2_version_cid version_cid;
+ int rv;
+
+ rv = ngtcp2_pkt_decode_version_cid (&version_cid, data, datalen,
+ NGTCP2_MAX_CIDLEN);
+ switch (rv)
+ {
+ case 0:
+ break;
+ case NGTCP2_ERR_VERSION_NEGOTIATION:
+ // TODO: send version negotiation
+ return;
+ default:
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "Can't decode version and CID: %s\n",
+ ngtcp2_strerror (rv));
+ return;
+ }
+
+ if (NULL == connection)
+ {
+ ngtcp2_pkt_hd header;
+ rv = ngtcp2_accept (&header, data, datalen);
+ if (0 != rv)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+ "ngtcp2_accept: %s\n",
+ ngtcp2_strerror (rv));
+ return;
+ }
+
+ /**
+ * TODO: handle the stateless reset token.
+ */
+
+ connection = connection_init (local_addr, local_addrlen, remote_addr,
+ remote_addrlen, &header.scid, &header.dcid,
+ header.version);
+ if (NULL == connection)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "accept connection error!\n");
+ return;
+ }
+
+ GNUNET_CONTAINER_multihashmap_put (addr_map,
+ addr_key,
+ connection,
+
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
+ rv = connection_on_read (connection, local_addr, local_addrlen,
remote_addr,
+ remote_addrlen, pi, data, datalen);
+ switch (rv)
+ {
+ case 0:
+ break;
+ case NETWORK_ERR_RETRY:
+ // TODO: send retry
+ return;
+ default:
+ return;
+ }
+
+ rv = connection_write (connection);
+ if (GNUNET_NO != rv)
+ {
+ return;
+ }
+
+ // add to cid_map here
+ return;
+ }
+
+ if (ngtcp2_conn_in_closing_period (connection->conn))
+ {
+ rv = send_conn_close (connection);
+ if (GNUNET_NO != rv)
+ {
+ connection_destroy (connection);
+ }
+ return;
+ }
+
+ if (ngtcp2_conn_in_draining_period (connection->conn))
+ {
+ return;
+ }
+
+ rv = connection_on_read (connection, local_addr, local_addrlen, remote_addr,
+ remote_addrlen, pi, data, datalen);
+ if (GNUNET_NO != rv)
+ {
+ if (NETWORK_ERR_CLOSE_WAIT != rv)
+ {
+ connection_destroy (connection);
+ }
+ return;
+ }
+
+ connection_write (connection);
+}
+
+
/**
* Socket read task.
*
@@ -2099,6 +2559,37 @@ sock_read (void *cls)
// }
// connection = cid_map_find (vc.scid, vc.scidlen);
// }
+
+
+ ngtcp2_pkt_info pi = {0};
+ if (NULL != connection && GNUNET_YES == connection->is_initiator)
+ {
+ rv = connection_feed_data (connection, local_addr, local_addrlen,
+ (struct sockaddr *) &sa,
+ salen, &pi, buf, rcvd);
+ if (GNUNET_NO != rv)
+ {
+ return;
+ }
+ rv = connection_write (connection);
+ if (rv < 0)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "connection write error!\n");
+ return;
+ }
+ continue;
+ }
+ else
+ {
+ server_read_pkt (connection, &addr_key,
+ local_addr, local_addrlen,
+ (struct sockaddr *) &sa, salen,
+ &pi, buf, rcvd);
+ }
+ continue;
+
+
if (NULL == connection)
{
connection = accept_connection (local_addr, local_addrlen,
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.
- [gnunet] 06/39: http3: handle mq, (continued)
- [gnunet] 06/39: http3: handle mq, gnunet, 2024/08/25
- [gnunet] 08/39: http3: do shutdown, gnunet, 2024/08/25
- [gnunet] 14/39: http3: rename COMMUNICATOR_ADDRESS_PREFIX, gnunet, 2024/08/25
- [gnunet] 19/39: http3: Add the functions to handle disconnection, gnunet, 2024/08/25
- [gnunet] 15/39: http3: Correct the value of statistics value # connections active, gnunet, 2024/08/25
- [gnunet] 09/39: http3: add http3 test, gnunet, 2024/08/25
- [gnunet] 10/39: http3: fix the log of ngtcp2_conn_writev_stream, gnunet, 2024/08/25
- [gnunet] 13/39: http3: extend flow control window, now can pass basic test., gnunet, 2024/08/25
- [gnunet] 22/39: http3: Complete the callbacks of nghttp3 and ngtcp2, gnunet, 2024/08/25
- [gnunet] 25/39: http3: make meson compie; remove application handle, gnunet, 2024/08/25
- [gnunet] 20/39: http3: Handle errors and refactor the code for receiving packets,
gnunet <=
- [gnunet] 26/39: http3: better http3 detection logic, gnunet, 2024/08/25
- [gnunet] 21/39: http3: Remove some unused code, gnunet, 2024/08/25
- [gnunet] 17/39: http3: Update TODOs, gnunet, 2024/08/25
- [gnunet] 11/39: http3: use multiple streams, gnunet, 2024/08/25
- [gnunet] 30/39: http3: Autogenerate certificate when there is no certificate in the configuration file., gnunet, 2024/08/25
- [gnunet] 24/39: http3: Finish HTTP3 layer and now can pass the basic test., gnunet, 2024/08/25
- [gnunet] 23/39: http3: Fix bugs when creating a new HTTP server, gnunet, 2024/08/25
- [gnunet] 18/39: http3: add the timer after sending and writing packets, gnunet, 2024/08/25
- [gnunet] 36/39: http3: Implement bidirect and pass the test., gnunet, 2024/08/25
- [gnunet] 27/39: http3: better http3 detection logic, gnunet, 2024/08/25