QUIC QTX: Handle network errors explicitly
authorHugo Landau <hlandau@openssl.org>
Mon, 9 Jan 2023 15:44:42 +0000 (15:44 +0000)
committerHugo Landau <hlandau@openssl.org>
Fri, 13 Jan 2023 13:20:37 +0000 (13:20 +0000)
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/19703)

include/internal/quic_record_tx.h
ssl/quic/quic_record_tx.c

index d010a2f64a367b4234bbb84aae63a2f4881cf1f7..ba2cf7fe788e52dede31fee065b3009d557c1e98 100644 (file)
@@ -210,8 +210,27 @@ void ossl_qtx_finish_dgram(OSSL_QTX *qtx);
  * is desired. The queue is drained into the OS's sockets as much as possible.
  * To determine if there is still data to be sent after calling this function,
  * use ossl_qtx_get_queue_len_bytes().
+ *
+ * Returns one of the following values:
+ *
+ *   QTX_FLUSH_NET_RES_OK
+ *      Either no packets are currently queued for transmission,
+ *      or at least one packet was successfully submitted.
+ *
+ *   QTX_FLUSH_NET_RES_TRANSIENT_FAIL
+ *      The underlying network write BIO indicated a transient error
+ *      (e.g. buffers full).
+ *
+ *   QTX_FLUSH_NET_RES_PERMANENT_FAIL
+ *      Internal error (e.g. assertion or allocation error)
+ *      or the underlying network write BIO indicated a non-transient
+ *      error.
  */
-void ossl_qtx_flush_net(OSSL_QTX *qtx);
+#define QTX_FLUSH_NET_RES_OK                1
+#define QTX_FLUSH_NET_RES_TRANSIENT_FAIL    (-1)
+#define QTX_FLUSH_NET_RES_PERMANENT_FAIL    (-2)
+
+int ossl_qtx_flush_net(OSSL_QTX *qtx);
 
 /*
  * Diagnostic function. If there is any datagram pending transmission, pops it
index 0cea8c13f57c15f2fa7bb4f1e95be7255ea11a88..dddb29663cab34d9d5d25b7b2c564cd3478e32ee 100644 (file)
@@ -812,14 +812,18 @@ static void txe_to_msg(TXE *txe, BIO_MSG *msg)
 
 #define MAX_MSGS_PER_SEND   32
 
-void ossl_qtx_flush_net(OSSL_QTX *qtx)
+int ossl_qtx_flush_net(OSSL_QTX *qtx)
 {
     BIO_MSG msg[MAX_MSGS_PER_SEND];
-    size_t wr, i;
+    size_t wr, i, total_written = 0;
     TXE *txe;
+    int res;
+
+    if (ossl_list_txe_head(&qtx->pending) == NULL)
+        return QTX_FLUSH_NET_RES_OK; /* Nothing to send. */
 
     if (qtx->bio == NULL)
-        return;
+        return QTX_FLUSH_NET_RES_PERMANENT_FAIL;
 
     for (;;) {
         for (txe = ossl_list_txe_head(&qtx->pending), i = 0;
@@ -829,21 +833,46 @@ void ossl_qtx_flush_net(OSSL_QTX *qtx)
 
         if (!i)
             /* Nothing to send. */
-            return;
+            break;
 
-        if (!BIO_sendmmsg(qtx->bio, msg, sizeof(BIO_MSG), i, 0, &wr) || wr == 0)
+        ERR_set_mark();
+        res = BIO_sendmmsg(qtx->bio, msg, sizeof(BIO_MSG), i, 0, &wr);
+        if (res && wr == 0) {
+            /*
+             * Treat 0 messages sent as a transient error and just stop for now.
+             */
+            ERR_clear_last_mark();
+            break;
+        } else if (!res) {
             /*
              * We did not get anything, so further calls will probably not
              * succeed either.
              */
-            break;
+            if (BIO_err_is_non_fatal(ERR_peek_last_error())) {
+                /* Transient error, just stop for now, clearing the error. */
+                ERR_pop_to_mark();
+                break;
+            } else {
+                /* Non-transient error, fail and do not clear the error. */
+                ERR_clear_last_mark();
+                return QTX_FLUSH_NET_RES_PERMANENT_FAIL;
+            }
+        }
+
+        ERR_clear_last_mark();
 
         /*
          * Remove everything which was successfully sent from the pending queue.
          */
         for (i = 0; i < wr; ++i)
             qtx_pending_to_free(qtx);
+
+        total_written += wr;
     }
+
+    return total_written > 0
+        ? QTX_FLUSH_NET_RES_OK
+        : QTX_FLUSH_NET_RES_TRANSIENT_FAIL;
 }
 
 int ossl_qtx_pop_net(OSSL_QTX *qtx, BIO_MSG *msg)