/* Returns 1 if the port is running/healthy, 0 if it has failed. */
int ossl_quic_port_is_running(const QUIC_PORT *port);
+/*
+ * Restores port-level error to the error stack. To be called only if
+ * the port is no longer running.
+ */
+void ossl_quic_port_restore_err_state(const QUIC_PORT *port);
+
/*
* Events
* ======
/*
* Called if a permanent network error occurs. Terminates all channels
- * immediately.
+ * immediately. triggering_ch is an optional argument designating
+ * a channel which encountered the network error.
*/
-void ossl_quic_port_raise_net_error(QUIC_PORT *port);
+void ossl_quic_port_raise_net_error(QUIC_PORT *port,
+ QUIC_CHANNEL *triggering_ch);
# endif
case QTX_FLUSH_NET_RES_PERMANENT_FAIL:
default:
/* Permanent underlying network BIO, start terminating. */
- ossl_quic_port_raise_net_error(ch->port);
+ ossl_quic_port_raise_net_error(ch->port, ch);
break;
}
{
QUIC_TERMINATE_CAUSE tcause = {0};
- ch->net_error = 1;
+ if (ch->net_error)
+ return;
- ERR_raise_data(ERR_LIB_SSL, SSL_R_QUIC_NETWORK_ERROR,
- "connection terminated due to network error");
- ch_save_err_state(ch);
+ ch->net_error = 1;
tcause.error_code = QUIC_ERR_INTERNAL_ERROR;
+ tcause.reason = "network BIO I/O error";
+ tcause.reason_len = strlen(tcause.reason);
/*
* Skip Terminating state and go directly to Terminated, no point trying to
if (ch == NULL)
return;
- OSSL_ERR_STATE_restore(ch->err_state);
+ if (!ossl_quic_port_is_running(ch->port))
+ ossl_quic_port_restore_err_state(ch->port);
+ else
+ OSSL_ERR_STATE_restore(ch->err_state);
}
void ossl_quic_channel_raise_protocol_error_loc(QUIC_CHANNEL *ch,
if (port->channel_ctx == NULL)
goto err;
+ if ((port->err_state = OSSL_ERR_STATE_new()) == NULL)
+ goto err;
+
if ((port->demux = ossl_quic_demux_new(/*BIO=*/NULL,
/*Short CID Len=*/rx_short_dcid_len,
get_time, port)) == NULL)
ossl_quic_lcidm_free(port->lcidm);
port->lcidm = NULL;
+
+ OSSL_ERR_STATE_free(port->err_state);
+ port->err_state = NULL;
}
static void port_transition_failed(QUIC_PORT *port)
if (!port->inhibit_tick) {
/* Handle any incoming data from network. */
- port_rx_pre(port);
+ if (ossl_quic_port_is_running(port))
+ port_rx_pre(port);
/* Iterate through all channels and service them. */
LIST_FOREACH(ch, ch, &port->channel_list) {
* Terminated state as there is no point trying to send CONNECTION_CLOSE
* frames if the network BIO is not operating correctly.
*/
- ossl_quic_port_raise_net_error(port);
+ ossl_quic_port_raise_net_error(port, NULL);
}
/*
port->inhibit_tick = (inhibit != 0);
}
-void ossl_quic_port_raise_net_error(QUIC_PORT *port)
+void ossl_quic_port_raise_net_error(QUIC_PORT *port,
+ QUIC_CHANNEL *triggering_ch)
{
QUIC_CHANNEL *ch;
+ if (!ossl_quic_port_is_running(port))
+ return;
+
+ /*
+ * Immediately capture any triggering error on the error stack, with a
+ * cover error.
+ */
+ ERR_raise_data(ERR_LIB_SSL, SSL_R_QUIC_NETWORK_ERROR,
+ "port failed due to network BIO I/O error");
+ OSSL_ERR_STATE_save(port->err_state);
+
port_transition_failed(port);
+ /* Give the triggering channel (if any) the first notification. */
+ if (triggering_ch != NULL)
+ ossl_quic_channel_raise_net_error(triggering_ch);
+
LIST_FOREACH(ch, ch, &port->channel_list)
- ossl_quic_channel_raise_net_error(ch);
+ if (ch != triggering_ch)
+ ossl_quic_channel_raise_net_error(ch);
+}
+
+void ossl_quic_port_restore_err_state(const QUIC_PORT *port)
+{
+ ERR_clear_error();
+ OSSL_ERR_STATE_restore(port->err_state);
}