QUIC PORT: Fix BIO_dgram usage under Winsock due to bind requirement
authorHugo Landau <hlandau@openssl.org>
Thu, 9 Nov 2023 15:30:15 +0000 (15:30 +0000)
committerHugo Landau <hlandau@openssl.org>
Thu, 21 Dec 2023 08:12:06 +0000 (08:12 +0000)
Reviewed-by: Tomas Mraz <tomas@openssl.org>
Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/22674)

ssl/quic/quic_channel.c
ssl/quic/quic_port.c
ssl/quic/quic_port_local.h

index 5bcd66c9e45ac750db13fe4ae12c7b30fde73e9b..60bcc88f84cf48ce429cec8193e17c0632889dbd 100644 (file)
@@ -2255,6 +2255,7 @@ static int ch_tx(QUIC_CHANNEL *ch)
         res = ossl_quic_tx_packetiser_generate(ch->txp, &status);
         if (status.sent_pkt > 0) {
             ch->have_sent_any_pkt = 1; /* Packet(s) were sent */
+            ch->port->have_sent_any_pkt = 1;
 
             /*
             * RFC 9000 s. 10.1. 'An endpoint also restarts its idle timer when
index e6dba46bf4aba5aac1d409c66866ff7c16b5bded..46f4b34f9bd6e9565ad1bf201ebe2828a0452f95 100644 (file)
@@ -323,6 +323,7 @@ QUIC_CHANNEL *ossl_quic_port_create_incoming(QUIC_PORT *port, SSL *tls)
 
     ch = port_make_channel(port, tls, /*is_server=*/1);
     port->tserver_ch = ch;
+    port->is_server  = 1;
     return ch;
 }
 
@@ -365,6 +366,25 @@ static void port_rx_pre(QUIC_PORT *port)
 {
     int ret;
 
+    /*
+     * Originally, this check (don't RX before we have sent anything if we are
+     * not a server, because there can't be anything) was just intended as a
+     * minor optimisation. However, it is actually required on Windows, and
+     * removing this check will cause Windows to break.
+     *
+     * The reason is that under Win32, recvfrom() does not work on a UDP socket
+     * which has not had bind() called (???). However, calling sendto() will
+     * automatically bind an unbound UDP socket. Therefore, if we call a Winsock
+     * recv-type function before calling a Winsock send-type function, that call
+     * will fail with WSAEINVAL, which we will regard as a permanent network
+     * error.
+     *
+     * Therefore, this check is essential as we do not require our API users to
+     * bind a socket first when using the API in client mode.
+     */
+    if (!port->is_server && !port->have_sent_any_pkt)
+        return;
+
     /*
      * Get DEMUX to BIO_recvmmsg from the network and queue incoming datagrams
      * to the appropriate QRX instances.
index 38bb0193d8ac9bf11360a3fe46193e634a1c3466..968a48ac4204cfc51e28c01a4da6960fbe4e786b 100644 (file)
@@ -87,6 +87,12 @@ struct quic_port_st {
 
     /* Inhibit tick for testing purposes? */
     unsigned int                    inhibit_tick                    : 1;
+
+    /* Has this port sent any packet of any kind yet? */
+    unsigned int                    have_sent_any_pkt               : 1;
+
+    /* Does this port allow incoming connections? */
+    unsigned int                    is_server                       : 1;
 };
 
 # endif