]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
openssl-quic: ignore unexpected streams opened by server
authorStefan Eissing <stefan@eissing.org>
Wed, 1 Oct 2025 09:23:27 +0000 (11:23 +0200)
committerDaniel Stenberg <daniel@haxx.se>
Wed, 1 Oct 2025 13:56:58 +0000 (15:56 +0200)
HTTP/3 defines "reserved stream types" that are intended to be ignored
by a receiver. This is part of the "greasing" effort that flexes parts
of the protocol that are needed for future extensions.

curl's OpenSSL-QUIC implementation treated all unexpected streams as
an error. Which seems the right thing to do *but* for these reserved types.
However OpenSSL does not expose this type and thus, curl needs to silently
discard all unexpected streams opened by the server to allow interop
with servers that flex the GREASE parts.

Fixes #18780
Reported-by: Pocs Norbert
Closes #18791

lib/vquic/curl_osslq.c

index e6278ad961a95b137d085f10047a1e95933d870a..aea03c038fb6600afd7596ea66cd8bda9f6756cf 100644 (file)
@@ -453,32 +453,36 @@ static CURLcode cf_osslq_h3conn_add_stream(struct cf_osslq_h3conn *h3,
 {
   struct cf_osslq_ctx *ctx = cf->ctx;
   curl_int64_t stream_id = (curl_int64_t)SSL_get_stream_id(stream_ssl);
-
-  if(h3->remote_ctrl_n >= CURL_ARRAYSIZE(h3->remote_ctrl)) {
-    /* rejected, we are full */
-    CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] rejecting remote stream",
-                stream_id);
-    SSL_free(stream_ssl);
-    return CURLE_FAILED_INIT;
-  }
-  switch(SSL_get_stream_type(stream_ssl)) {
+  int stype = SSL_get_stream_type(stream_ssl);
+  /* This could be a GREASE stream, e.g. HTTP/3 rfc9114 ch 6.2.3
+   * reserved stream type that is supposed to be discarded silently.
+   * BUT OpenSSL does not offer this information to us. So, we silently
+   * ignore all such streams we do not expect. */
+  switch(stype) {
     case SSL_STREAM_TYPE_READ: {
-      struct cf_osslq_stream *nstream = &h3->remote_ctrl[h3->remote_ctrl_n++];
+      struct cf_osslq_stream *nstream;
+      if(h3->remote_ctrl_n >= CURL_ARRAYSIZE(h3->remote_ctrl)) {
+        /* rejected, we are full */
+        CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] reject remote uni stream",
+                    stream_id);
+        SSL_free(stream_ssl);
+        return CURLE_OK;
+      }
+      nstream = &h3->remote_ctrl[h3->remote_ctrl_n++];
       nstream->id = stream_id;
       nstream->ssl = stream_ssl;
       Curl_bufq_initp(&nstream->recvbuf, &ctx->stream_bufcp, 1, BUFQ_OPT_NONE);
       CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] accepted remote uni stream",
                   stream_id);
-      break;
+      return CURLE_OK;
     }
     default:
-      CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] reject remote non-uni-read"
-                  " stream", stream_id);
+      CURL_TRC_CF(data, cf, "[%" FMT_PRId64 "] reject remote %s"
+                  " stream, type=%x", stream_id,
+                  (stype == SSL_STREAM_TYPE_BIDI) ? "bidi" : "write", stype);
       SSL_free(stream_ssl);
-      return CURLE_FAILED_INIT;
+      return CURLE_OK;
   }
-  return CURLE_OK;
-
 }
 
 static CURLcode cf_osslq_ssl_err(struct Curl_cfilter *cf,