Skip to content

Conversation

santigimeno
Copy link
Member

@santigimeno santigimeno commented Sep 22, 2025

Summary by CodeRabbit

  • New Features
    • Faster connections with Happy Eyeballs across IPv4/IPv6.
    • WebSockets: add curl_ws_start_frame().
    • Multi interface: new curl_multi_get_offt() and CURLMOPT_NETWORK_CHANGED.
  • API Changes
    • Bump to libcurl 8.16.0.
    • Expanded compile-time/type checks for curl_multi_setopt.
  • Improvements
    • More precise DNS cache timing and pruning.
    • Better ALPN/proxy handling; broader HTTP/2 proxy coverage.
    • Smarter cookie “secure” context detection.
  • Bug Fixes
    • Safer HMAC initialization, date parsing, IDN zero-length handling, and buffer overflow guards.
  • Chores
    • Windows uses local inet_ntop/inet_pton; remove MSH3; stricter static analysis.

@santigimeno santigimeno self-assigned this Sep 22, 2025
Copy link

coderabbitai bot commented Sep 22, 2025

Walkthrough

Introduces a libcurl 8.16.0 update with extensive internal API refactors from getsock to pollset, new IP “Happy Eyeballs” connection filter, DNS/async resolver updates, new multi handle metrics/query, header/enum literal normalization, cookie and parsedate API changes, build/config updates, tracing additions, and multiple protocol handler adaptations.

Changes

Cohort / File(s) Summary
EditorConfig
deps/curl/.editorconfig
Add root EditorConfig with charset/indent/newline rules; per-language indent/line-length.
Build system and variables
deps/curl/include/Makefile.in, deps/curl/include/curl/Makefile.in, deps/curl/lib/CMakeLists.txt, deps/curl/lib/Makefile.am, deps/curl/lib/Makefile.in, deps/curl/lib/Makefile.inc
Add DANTED variable; adjust CMake compile definitions and LTO properties; remove ALL from custom target; wire in new cf-ip-happy.{c,h}; update dist/dep lists; tidy flags; remove msh3 sources/defines; add curl_mem_undef.h.
Version bump
deps/curl/include/curl/curlver.h
Bump libcurl to 8.16.0; update numeric/version timestamp macros.
Public headers formatting/guards
.../curl/easy.h, .../curl/header.h, .../curl/mprintf.h, .../curl/options.h, .../curl/system.h, .../curl/urlapi.h
Normalize preprocessor guards and minor style; no API changes (except narrowed MINGW macro check in mprintf.h).
Public API constants normalization
deps/curl/include/curl/curl.h, deps/curl/include/curl/websockets.h
Convert many macros/enums to long-suffixed literals; add LAST sentinels; adjust bitmasks; add curl_ws_start_frame API and update WS flags to 1L-based.
Pollset abstraction migration (core)
deps/curl/lib/select.{h,c}, deps/curl/lib/multi*.{h,c}, deps/curl/lib/cfilters.{h,c}, deps/curl/lib/pingpong.{h,c}
Introduce struct easy_pollset and full pollset API; replace getsock paths; change adjust_pollset hooks to return CURLcode; add ALPN query; add multi pollset integration and dirty/metrics helpers.
Pollset migration (protocol handlers)
deps/curl/lib/{ftp.c,imap.c,pop3.c,mqtt.c,rtsp.c,gopher.c,ldap.c,openldap.c,file.c,curl_rtmp.c}
Replace proto/doing/domore/perform_getsock with pollset variants; add per-protocol pollset functions; update transfer setup calls to new helpers.
DNS/resolver and async overhaul
deps/curl/lib/{hostip*.c,h}, deps/curl/lib/asyn-*.c, deps/curl/lib/asyn.h, deps/curl/lib/doh.{c,h}
Switch getsock→pollset for resolver; millisecond DNS cache aging; add Curl_dnscache_clear; expand Curl_resolver_error(detail); ares/threaded lifecycle refactors; rename DNS_TYPE_* to CURL_DNS_TYPE_*; add async pollset APIs/state.
New IP Happy Eyeballs filter
deps/curl/lib/cf-ip-happy.{c,h}, deps/curl/lib/connect.{c,h}
Add “HAPPY-EYEBALLS” cfilter, insertion API, and tracing; move/rename insertion helper; adjust connect setup; export header.
HTTP path refactor
deps/curl/lib/http.{c,h}, deps/curl/lib/http2.c, deps/curl/lib/http_proxy.c, deps/curl/lib/http_aws_sigv4.c, deps/curl/lib/http_chunks.c
Modularize header building, TE handling; remove conn param from Curl_http_method; add Curl_http_do_pollset; adjust H2 guards/callback sizes; unify chunk reader control hook.
Cookies and parsedate API
deps/curl/lib/cookie.{c,h}, deps/curl/lib/parsedate.{c,h}
Cookie getlist now uses connectdata context and host; add Curl_secure_context; Curl_getdate_capped now int-return with out-param.
Multi API extensions
deps/curl/include/curl/multi.h, deps/curl/lib/libcurl.def
Add CURLMOPT_NETWORK_CHANGED, flags/macros; add CURLMinfo_offt enum and curl_multi_get_offt(); export new symbols.
Typechecking for multi_setopt
deps/curl/include/curl/typecheck-gcc.h
Add curl_multi_setopt typecheck macro and related helpers/warnings; add ptr-to-ptr checker.
Tracing additions
deps/curl/lib/curl_trc.{h,c}
Add timer name lookup and multi timeouts tracing; new macros; update features table.
Sockets/cfilters adjustments
deps/curl/lib/cf-*.{c,h}, deps/curl/lib/cf-socket.{c,h}
Many adjust_pollset hooks now return CURLcode; internalize Curl_sock_assign_addr; minor guard tweaks; ALPN query case added in H2 proxy.
Threads API
deps/curl/lib/curl_threads.{h,c}
Change Curl_thread_create func type to CURL_THREAD_RETURN_T/CALL; add Curl_thread_cancel and cleanup/cancel macros.
Security/crypto tweaks
deps/curl/lib/{hmac.c,curl_hmac.h,md4.c,md5.c,curl_ntlm_core.{c,h},curl_md*.h}
Adjust guards; HMAC init error checks; NTLM HMAC signature adds output param; backend/version gating tightened; no public symbol removals beyond header changes.
Windows/Config updates
deps/curl/lib/config-win32.h, deps/curl/lib/curl_setup{,_once}.h, deps/curl/lib/curl_mem_undef.h, deps/curl/lib/curl_memory.h
Undef inet_ntop/pton availability; add memory callback typedefs and debug wrappers in setup.h; remove in curl_memory.h; macro undef consolidations; remove sclose alias.
Conn cache/network-changed
deps/curl/lib/conncache.{c,h}
Add Curl_cpool_nw_changed to mark/reap connections on network change; tweak dead/stale pruning.
Progress/request/cw-out
deps/curl/lib/{progress.c,request.{c,h},cw-out.c}
Rate-limit wait calc updated; SingleRequest getheader flag removed; support 0-length client writes via new path.
Misc small/style updates
.../altsvc.c, content_encoding.c, curl_addrinfo.c, curl_gethostname.c, curl_gssapi.c, curl_ntlm_core.c, curl_rtmp.c, curl_sasl.c, dllmain.c, easy.c, easy_lock.h, easygetopt.c, fileinfo.c, fopen.c, formdata.c, gss/krb/ldap/openldap helpers, idn.c, if2ip.c, netrc.{c,h}, noproxy.c, rand.c, select.c helpers, sendf.{c,h}, curlx/*, etc.
Style/guards/logic cleanups; minor behavior fixes (IDN zero-length decode, HSTS/Alt-Svc date parsing via new API); update inet_ntop/pton declarations paths; adjust various helpers to new APIs.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor App
  participant Multi as curl_multi
  participant Easy as CURL easy
  participant Poll as easy_pollset
  participant Proto as Protocol handler
  participant CF as Conn filters
  participant OS as Sockets

  App->>Multi: add_handle(easy)
  Multi->>Easy: state machine tick
  Easy->>Poll: Curl_pollset_init/reset
  Easy->>Proto: proto_*_pollset(ps)
  Proto->>CF: Curl_conn_cf_adjust_pollset(ps)
  CF->>Poll: set/change sockets (IN/OUT)
  Easy->>OS: Curl_pollset_poll(timeout)
  OS-->>Easy: readiness events
  Easy->>Proto: perform_*_pollset(ps)
  Proto->>CF: send/recv via filters
  CF-->>Proto: data/complete
  Proto-->>Easy: progress/status
  Easy-->>Multi: updates (xfers_running, pending)
  Multi-->>App: events/callbacks
Loading
sequenceDiagram
  autonumber
  actor App
  participant CFH as CF: IP Happy Eyeballs
  participant T4 as Attempt IPv4
  participant T6 as Attempt IPv6
  participant Net as Network

  App->>CFH: insert after (transport)
  CFH->>T6: start_connect()
  CFH-->>T4: delayed start_connect()
  T6->>Net: connect()
  T4->>Net: connect()
  alt first success
    T6-->>CFH: connected
    CFH-->>T4: cancel/close
    CFH-->>App: connected (winner)
  else both fail/time out
    T6-->>CFH: error
    T4-->>CFH: error
    CFH-->>App: CURLE_COULDNT_CONNECT
  end
Loading
sequenceDiagram
  autonumber
  participant Easy as CURL easy
  participant DNS as Resolver (sync/ares/thread)
  participant PS as easy_pollset
  participant Cache as DNS cache

  Easy->>DNS: Curl_resolv(...)
  DNS->>PS: resolv_pollset(ps)
  Easy->>PS: poll(timeout)
  PS-->>DNS: ready
  DNS->>Cache: store (positive/negative)
  DNS-->>Easy: result or error(detail)
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~150 minutes

Poem

I twitched my ears at sockets’ song,
Pollsets danced the whole day long.
Happy eyes chased v4 and v6,
Picked the fastest clever fix.
New timers tick, the cookies grin—
8.16.0 hops right in.
Thump-thump! Ship it. 🐇✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 34.09% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "deps: update libcurl to 8.16.0" is concise, a single sentence, and directly describes the primary change in the PR (bumping the libcurl dependency to 8.16.0), which the raw summary and PR objectives confirm; it is clear and appropriate for teammates scanning history.
✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch santi/update_curl

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
deps/curl/lib/krb5.c (1)

559-563: Critical: pass dynbuf length to conn->mech->decode (avoid passing zero 'len')

len is decremented to 0 in the read loop; passing that 0 to conn->mech->decode can break GSS unwrap. Upstream curl 8.16.0 (rc1) still calls decode with the decremented len (not the dynbuf length), so apply the local fix below. (fossies.org)

File: deps/curl/lib/krb5.c
Lines: 559-563

Apply:

-  /* this decodes the dynbuf *in place* */
-  nread = conn->mech->decode(conn->app_data,
-                             curlx_dyn_ptr(&buf->buf),
-                             len, conn->data_prot, conn);
-  if(nread < 0)
+  /* this decodes the dynbuf *in place* */
+  {
+    size_t enc_sz = curlx_dyn_len(&buf->buf);
+    if(enc_sz > (size_t)INT_MAX)
+      return CURLE_TOO_LARGE;
+    nread = conn->mech->decode(conn->app_data,
+                               curlx_dyn_ptr(&buf->buf),
+                               curlx_uztosi(enc_sz), conn->data_prot, conn);
+  }
+  if(nread < 0)
     return CURLE_RECV_ERROR;
   Curl_dyn_setlen(&buf->buf, nread);
   buf->index = 0;
   return CURLE_OK;
deps/curl/lib/curlx/dynbuf.c (1)

76-89: Prevent size_t overflow in fit calculation.

fit = len + idx + 1 can wrap before the fit > s->toobig check. Guard with subtraction first.

Apply:

-  size_t fit = len + idx + 1; /* new string + old string + zero byte */
+  size_t fit; /* new string + old string + zero byte */
+  if(len > s->toobig - idx - 1) {
+    curlx_dyn_free(s);
+    return CURLE_TOO_LARGE;
+  }
+  fit = idx + len + 1;
deps/curl/lib/cf-haproxy.c (1)

227-228: Likely typo: DYN_HAXPROXY

Assuming the dynbuf tag is DYN_HAPROXY; current spelling may not compile if undefined.

-  curlx_dyn_init(&ctx->data_out, DYN_HAXPROXY);
+  curlx_dyn_init(&ctx->data_out, DYN_HAPROXY);

If the intended tag differs, please align with the defined constant in dynbuf tags.

deps/curl/lib/rtsp.c (1)

177-196: Free allocations on meta‑set failure (memory leak).

If Curl_conn_meta_set() or Curl_meta_set() fails, the newly allocated rtspc/rtsp are leaked. Free them before returning.

Apply this diff:

 static CURLcode rtsp_setup_connection(struct Curl_easy *data,
                                       struct connectdata *conn)
 {
   struct rtsp_conn *rtspc;
   struct RTSP *rtsp;

   rtspc = calloc(1, sizeof(*rtspc));
   if(!rtspc)
     return CURLE_OUT_OF_MEMORY;
   curlx_dyn_init(&rtspc->buf, MAX_RTP_BUFFERSIZE);
-  if(Curl_conn_meta_set(conn, CURL_META_RTSP_CONN, rtspc, rtsp_conn_dtor))
-    return CURLE_OUT_OF_MEMORY;
+  if(Curl_conn_meta_set(conn, CURL_META_RTSP_CONN, rtspc, rtsp_conn_dtor)) {
+    curlx_dyn_free(&rtspc->buf);
+    free(rtspc);
+    return CURLE_OUT_OF_MEMORY;
+  }

   rtsp = calloc(1, sizeof(struct RTSP));
-  if(!rtsp ||
-     Curl_meta_set(data, CURL_META_RTSP_EASY, rtsp, rtsp_easy_dtor))
-    return CURLE_OUT_OF_MEMORY;
+  if(!rtsp ||
+     Curl_meta_set(data, CURL_META_RTSP_EASY, rtsp, rtsp_easy_dtor)) {
+    if(rtsp)
+      free(rtsp);
+    /* rtspc is owned by conn meta and will be freed by its dtor */
+    return CURLE_OUT_OF_MEMORY;
+  }

   return CURLE_OK;
 }
deps/curl/lib/http.c (1)

1490-1497: Critical: Curl_compareheader only matches at start-of-value (regression).

The loop condition uses a fixed upper bound, so it checks only the first position and never scans the whole header value. This breaks cases where the token is not at offset 0.

Apply this diff to scan the entire value:

-  if(curlx_strlen(&val) >= clen) {
-    size_t len;
-    p = curlx_str(&val);
-    for(len = curlx_strlen(&val); len >= curlx_strlen(&val); len--, p++) {
-      if(curl_strnequal(p, content, clen))
-        return TRUE; /* match! */
-    }
-  }
+  if(curlx_strlen(&val) >= clen) {
+    size_t vlen = curlx_strlen(&val);
+    const char *vp = curlx_str(&val);
+    size_t i;
+    for(i = 0; i + clen <= vlen; ++i) {
+      if(curl_strnequal(vp + i, content, clen))
+        return TRUE; /* match! */
+    }
+  }
🧹 Nitpick comments (73)
deps/curl/lib/curlx/wait.c (1)

71-73: Clamp MSDOS delay to avoid unsigned int overflow

delay((unsigned int)timeout_ms); may wrap for large timeouts. Mirror the Windows path by clamping to UINT_MAX - 1.

Apply this diff:

-#ifdef MSDOS
-  delay((unsigned int)timeout_ms);
+#ifdef MSDOS
+#if TIMEDIFF_T_MAX >= UINT_MAX
+  if(timeout_ms >= UINT_MAX)
+    timeout_ms = UINT_MAX-1;
+#endif
+  delay((unsigned int)timeout_ms);
deps/curl/lib/curlx/multibyte.h (1)

29-32: Doc-only tweak looks fine

Single-line comment and guard scope remain correct; no API/ABI impact.

Optionally keep trailing context on the guard for readability:

-#endif
+#endif /* _WIN32 */
deps/curl/lib/curl_gethostname.c (1)

84-84: Guard against namelen == 0 before writing the terminator.

name[namelen - 1] = '\0'; can underflow when namelen is 0. Add a cheap check.

Apply this diff:

-  name[namelen - 1] = '\0';
+  if(namelen)
+    name[namelen - 1] = '\0';
+  else
+    return 1;

Please confirm callers never pass namelen == 0; if guaranteed by contract, consider a DEBUGBUILD assert instead.

deps/curl/lib/curl_addrinfo.c (1)

486-516: Fix mismatched trailing #endif comment.

The block opens with #if defined(CURLDEBUG) && defined(HAVE_GETADDRINFO) && defined(HAVE_FREEADDRINFO) but ends with /* CURLDEBUG && HAVE_FREEADDRINFO */. Align the comment for clarity.

Apply:

-#endif /* CURLDEBUG && HAVE_FREEADDRINFO */
+#endif /* CURLDEBUG && HAVE_GETADDRINFO && HAVE_FREEADDRINFO */
deps/curl/lib/curlx/timeval.c (2)

171-173: Make timebase init thread-safe.

This lazy init on a static timebase can race under concurrent calls to curlx_now(). Prefer one-time init.

Apply this diff within the function, and add the helper outside (see snippet below):

-  if(timebase.denom == 0)
-    (void)mach_timebase_info(&timebase);
+  pthread_once(&timebase_once, curlx_timebase_init);

Add near the top of this block:

#include <pthread.h>
static mach_timebase_info_data_t timebase;
static pthread_once_t timebase_once = PTHREAD_ONCE_INIT;
static void curlx_timebase_init(void) { (void)mach_timebase_info(&timebase); }

175-176: Guard against 64‑bit overflow in scale-to-ns step.

mach_absolute_time() * timebase.numer can overflow 64‑bit before the divide. Use 128‑bit when available, else divide first (precision loss is negligible at µs resolution).

Apply this diff:

-  usecs = mach_absolute_time();
-  usecs *= timebase.numer; /* spellchecker:disable-line */
-  usecs /= timebase.denom;
-  usecs /= 1000;
+  #if defined(__SIZEOF_INT128__)
+  {
+    __uint128_t n = ( (__uint128_t)mach_absolute_time() * timebase.numer ); /* spellchecker:disable-line */
+    n /= timebase.denom;
+    usecs = (uint64_t)(n / 1000);
+  }
+  #else
+  usecs = mach_absolute_time();
+  /* Reduce overflow risk by dividing first; µs rounding is acceptable. */
+  usecs = (usecs / timebase.denom) * timebase.numer; /* spellchecker:disable-line */
+  usecs /= 1000;
+  #endif
deps/curl/lib/idn.c (2)

155-167: Guard _WIN32_WINNT_VISTA absence to keep portability with older SDKs.

Some SDKs may not define _WIN32_WINNT_VISTA; when that happens the comparison evaluates against 0, potentially skipping these prototypes incorrectly. Use a fallback to 0x0600 in the expression and normalize WINVER comparison.

Apply this diff:

-#if (!defined(_WIN32_WINNT) || _WIN32_WINNT < _WIN32_WINNT_VISTA) && \
-  (!defined(WINVER) || WINVER < 0x600)
+#if (!defined(_WIN32_WINNT) || \
+     _WIN32_WINNT < (defined(_WIN32_WINNT_VISTA) ? _WIN32_WINNT_VISTA : 0x0600)) && \
+    (!defined(WINVER) || WINVER < 0x0600)

If you prefer not to use the ternary in a preproc expression, alternatively add a local fallback just above this block:

#ifndef _WIN32_WINNT_VISTA
#define _WIN32_WINNT_VISTA 0x0600
#endif

Please confirm Windows CI across all supported VS/SDK combos still compiles this TU without relying on <sdkddkver.h>.


326-333: Harden against unexpected NULL from idn_decode and clear output on error.

Defensive null‑check prevents UB if invariants ever regress; also explicitly null the out‑param on error.

Apply this diff:

-  if(!result) {
-    if(!d[0]) { /* ended up zero length, not acceptable */
-      result = CURLE_URL_MALFORMAT;
-      free(d);
-    }
-    else
-      *output = d;
-  }
+  if(!result) {
+    if(!d || !d[0]) { /* ended up NULL/zero length, not acceptable */
+      result = CURLE_URL_MALFORMAT;
+      free(d);
+      if(output) *output = NULL;
+    }
+    else {
+      *output = d;
+    }
+  }
deps/curl/lib/bufq.c (2)

176-181: Good overflow guard before allocation in bufcp_take.

Prevents size_t wrap when computing the allocation size. Optional: factor this pattern into a tiny helper to avoid duplication and keep intent consistent across sites.


310-314: Mirrored overflow guard in non‑pooled path looks correct.

Returning NULL here propagates as CURLE_OUT_OF_MEMORY at the call site, which is appropriate for overflow. Optional: consolidate both checks via a shared helper to keep behavior uniform.

deps/curl/lib/httpsrr.h (1)

29-31: Optional: narrow the ares include to the HTTPSRR feature block

Avoid including <ares.h> when USE_HTTPSRR is off (but USE_ARES is on) to reduce header coupling.

Apply:

-#ifdef USE_ARES
-#include <ares.h>
-#endif
+#ifdef USE_HTTPSRR
+  #ifdef USE_ARES
+  #include <ares.h>
+  #endif

Also applies to: 33-35

deps/curl/lib/curl_config.h.in (1)

68-69: Fix comment: says “alt-svc” but macro disables HSTS

The comment mismatches the macro name.

Apply:

-/* disable alt-svc */
+/* disable HSTS */
 #undef CURL_DISABLE_HSTS
deps/curl/lib/progress.c (1)

287-317: Guard against negative byte deltas when counters reset.

If d->cur_size < d->limit.start_size (e.g., counter reset/redirect), bytes becomes negative; should_ms then goes negative. It still returns 0, but add an explicit non‑negative guard to avoid surprising math.

-  curl_off_t bytes = d->cur_size - d->limit.start_size;
+  curl_off_t bytes = d->cur_size - d->limit.start_size;
   timediff_t should_ms;
   timediff_t took_ms;

-  /* no limit or we did not get to any bytes yet */
-  if(!bytes_per_sec || !bytes)
+  /* no limit or no positive progress yet */
+  if(!bytes_per_sec || bytes <= 0)
     return 0;
deps/curl/lib/curl_fnmatch.c (2)

81-99: Avoid counting ':' and ']' toward the keyword length (minor robustness)

i increments every loop iteration, so : and ] are included in the bound check. Not an issue today (all known keywords are ≤6 chars), but trivially avoidable. Increment i only when appending a letter and check against sizeof(keyword) - 1 before writing.

-  size_t i;
+  size_t i;
   const unsigned char *p = *pattern;
   bool found = FALSE;
-  for(i = 0; !found; i++) {
-    char c = (char)*p++;
-    if(i >= sizeof(keyword))
-      return SETCHARSET_FAIL;
+  for(i = 0; !found; ) {
+    char c = (char)*p++;
     switch(state) {
     case CURLFNM_PKW_INIT:
-      if(ISLOWER(c))
-        keyword[i] = c;
+      if(ISLOWER(c)) {
+        if(i == sizeof(keyword) - 1)
+          return SETCHARSET_FAIL;
+        keyword[i++] = c;
+      }
       else if(c == ':')
         state = CURLFNM_PKW_DDOT;
       else
         return SETCHARSET_FAIL;
       break;
     case CURLFNM_PKW_DDOT:
       if(c == ']')
         found = TRUE;
       else
         return SETCHARSET_FAIL;
+      break;
     }
   }

101-101: Remove stale #undef KEYLEN
KEYLEN is not defined in this file; the #undef is a harmless no-op and confusing — delete it.
deps/curl/lib/curl_fnmatch.c:101

deps/curl/lib/curl_sspi.c (1)

199-206: Zero sensitive password buffer before free

Freeing identity->Password without clearing leaves credentials in process memory. Wipe it first.

Note: if this file is vendored from upstream curl 8.16.0, consider submitting upstream or carrying as a minimal patch if acceptable.

 void Curl_sspi_free_identity(SEC_WINNT_AUTH_IDENTITY *identity)
 {
   if(identity) {
     Curl_safefree(identity->User);
-    Curl_safefree(identity->Password);
+    if(identity->Password && identity->PasswordLength) {
+      /* bytes = characters * sizeof(TCHAR) */
+      SecureZeroMemory(identity->Password,
+        (size_t)identity->PasswordLength * sizeof(TCHAR));
+    }
+    Curl_safefree(identity->Password);
     Curl_safefree(identity->Domain);
   }
 }

If you want, I can check upstream for an equivalent practice and open an issue/PR.

deps/curl/lib/mime.c (2)

2001-2026: Guard against partial bufq writes in the “small read” path

If Curl_bufq_write ever stores fewer than requested bytes (unlikely, but future‑proof), subsequent cread would underflow expectations. Add a strict check and fail early.

   else if(blen <= 4) {
@@
-    nread = Curl_mime_read(tmp, 1, sizeof(tmp), ctx->part);
+    nread = Curl_mime_read(tmp, 1, sizeof(tmp), ctx->part);
     if(nread <= sizeof(tmp)) {
       size_t n;
-      result = Curl_bufq_write(&ctx->tmpbuf, (unsigned char *)tmp, nread, &n);
+      result = Curl_bufq_write(&ctx->tmpbuf, (unsigned char *)tmp, nread, &n);
       if(result) {
         ctx->errored = TRUE;
         ctx->error_result = result;
         return result;
       }
+      if(n != nread) {
+        ctx->errored = TRUE;
+        ctx->error_result = CURLE_WRITE_ERROR;
+        return ctx->error_result;
+      }
       /* stored it, read again */
       result = Curl_bufq_cread(&ctx->tmpbuf, buf, blen, &nread);

2152-2173: Return CURLE_UNKNOWN_OPTION for unknown control opcodes

Search shows callers only use CURL_CRCNTRL_REWIND / CURL_CRCNTRL_UNPAUSE / CURL_CRCNTRL_CLEAR_EOS and none appear to rely on an implicit OK for unknown opcodes; this change is safe and improves error visibility.

  default:
-    break;
+    return CURLE_UNKNOWN_OPTION;
deps/curl/lib/curl_ntlm_core.c (2)

54-66: Crypto backend selection and AWS-LC shim: confirm portability

  • USE_OPENSSL_DES gating and OPENSSL_NO_DEPRECATED_3_0 handling look correct for OpenSSL 3.x.
  • The AWS-LC alias for DES_set_key_unchecked is reasonable; verify OPENSSL_IS_AWSLC is present across targeted AWS-LC versions, or guard with defined(...) to avoid unknown-macro warnings on some compilers.
  • WOLFSSL OPENSSL_COEXIST remaps are unchanged; OK.

If you hit portability warnings, consider a defensive check:

-#  ifdef OPENSSL_IS_AWSLC  /* for versions 1.2.0 to 1.30.1 */
+#  if defined(OPENSSL_IS_AWSLC)  /* for versions 1.2.0 to 1.30.1 */
 #    define DES_set_key_unchecked (void)DES_set_key
 #  endif

Also applies to: 69-77, 83-93, 94-96


569-577: Minor: avoid msnprintf for binary blob header or check return

msnprintf builds binary bytes via %c, which works, but silently truncates on unexpected sizes. Consider direct stores or at least validate the return.

-  msnprintf((char *)ptr + HMAC_MD5_LENGTH, NTLMv2_BLOB_LEN,
-            "%c%c%c%c"
-            "%c%c%c%c"
-            "%c%c%c%c%c%c%c%c",
-            NTLMv2_BLOB_SIGNATURE[0], NTLMv2_BLOB_SIGNATURE[1],
-            NTLMv2_BLOB_SIGNATURE[2], NTLMv2_BLOB_SIGNATURE[3],
-            0, 0, 0, 0,
-            LONGQUARTET(tw.dwLowDateTime), LONGQUARTET(tw.dwHighDateTime));
+  unsigned char *b = ptr + HMAC_MD5_LENGTH;
+  memcpy(b, NTLMv2_BLOB_SIGNATURE, 4);
+  memset(b + 4, 0, 4);
+  b[8]  = (unsigned char)(tw.dwLowDateTime & 0xFF);
+  b[9]  = (unsigned char)((tw.dwLowDateTime >> 8) & 0xFF);
+  b[10] = (unsigned char)((tw.dwLowDateTime >> 16) & 0xFF);
+  b[11] = (unsigned char)((tw.dwLowDateTime >> 24) & 0xFF);
+  b[12] = (unsigned char)(tw.dwHighDateTime & 0xFF);
+  b[13] = (unsigned char)((tw.dwHighDateTime >> 8) & 0xFF);
+  b[14] = (unsigned char)((tw.dwHighDateTime >> 16) & 0xFF);
+  b[15] = (unsigned char)((tw.dwHighDateTime >> 24) & 0xFF);
deps/curl/lib/krb5.c (2)

472-474: Nit: (void)nread likely unnecessary.

nread is used as an out-param to Curl_GetFTPResponse; most compilers won’t warn. Safe to keep, but could be dropped.

-  (void)nread;
+  /* nread intentionally unused beyond Curl_GetFTPResponse */

720-748: Remove redundant (void)data; it’s used later.

data is used by Curl_debug on Line 747; the cast is confusing and unnecessary.

-  (void)data;
+  /* data used below for Curl_debug */
deps/curl/lib/curl_threads.h (1)

69-69: New API Curl_thread_cancel: document return semantics.

It returns 0 on success (POSIX), 1 when not supported, and 0 when handle is null; that differs from Curl_thread_join’s boolean style. Add a short comment to avoid misuse.

 int Curl_thread_cancel(curl_thread_t *hnd);
+/* Returns:
+ *  0 on successful cancellation (POSIX with pthread_cancel),
+ *  1 when cancellation is not supported on the platform,
+ *  0 if the handle is null.
+ * Note: semantics differ from Curl_thread_join (boolean-style).
+ */
deps/curl/lib/curl_threads.c (3)

44-60: Align thunk types with the public macro and silence potential warnings.

Use CURL_THREAD_RETURN_T (CURL_STDCALL func)(void) in Curl_actual_call and cast the ignored return to void; also prefer returning NULL.

-struct Curl_actual_call {
-  unsigned int (*func)(void *);
-  void *arg;
-};
+struct Curl_actual_call {
+  CURL_THREAD_RETURN_T (CURL_STDCALL *func)(void *);
+  void *arg;
+};
@@
-static void *curl_thread_create_thunk(void *arg)
+static void *curl_thread_create_thunk(void *arg)
 {
   struct Curl_actual_call *ac = arg;
-  unsigned int (*func)(void *) = ac->func;
+  CURL_THREAD_RETURN_T (CURL_STDCALL *func)(void *) = ac->func;
   void *real_arg = ac->arg;
@@
-  (*func)(real_arg);
-
-  return 0;
+  (void)(*func)(real_arg);
+  return NULL;
}

119-129: Curl_thread_cancel (POSIX): clarify status codes and remove redundant cast.

Function returns 1 when not supported and 0 on null handle; consider documenting (see header comment) and drop the unused (void)hnd.

-int Curl_thread_cancel(curl_thread_t *hnd)
+int Curl_thread_cancel(curl_thread_t *hnd)
 {
-  (void)hnd;
   if(*hnd != curl_thread_t_null)
 #ifdef USE_PTHREAD_CANCEL
     return pthread_cancel(**hnd);
 #else
     return 1; /* not supported */
 #endif
   return 0;
 }

185-191: Curl_thread_cancel (Windows): explicit “not supported” return.

Returns 1 when handle is valid. Consider mirroring the header doc comment for consistency.

-int Curl_thread_cancel(curl_thread_t *hnd)
+int Curl_thread_cancel(curl_thread_t *hnd)
 {
   if(*hnd != curl_thread_t_null) {
-    return 1; /* not supported */
+    return 1; /* not supported: Windows threads cannot be asynchronously canceled */
   }
   return 0;
 }
deps/curl/lib/memdebug.c (1)

374-377: Keep function-name parentheses to avoid macro interception and stay consistent.

Elsewhere in this file you intentionally use (socket), (send), (recv), (fdopen), (fopen), (accept4) to bypass possible macro wrappers. Dropping them for accept is inconsistent and could bite if accept is ever macro-defined by a platform header.

Apply for consistency:

-  curl_socket_t sockfd = accept(s, addr, addrlen);
+  curl_socket_t sockfd = (accept)(s, addr, addrlen);
deps/curl/lib/curlx/dynbuf.c (1)

231-236: Null-safe debug assert for format string.

DEBUGASSERT(strcmp(fmt, "%s")); dereferences fmt in debug builds if fmt == NULL. Add a prior assert.

Apply:

   DEBUGASSERT(s->init == DYNINIT);
   DEBUGASSERT(!s->leng || s->bufr);
-  DEBUGASSERT(strcmp(fmt, "%s")); /* use curlx_dyn_add instead */
+  DEBUGASSERT(fmt);
+  DEBUGASSERT(strcmp(fmt, "%s")); /* use curlx_dyn_add instead */
deps/curl/lib/Makefile.am (1)

177-177: clang-tidy ArrayBound check disabled — confirm rationale.

Disabling -clang-analyzer-security.ArrayBound can hide real issues (esp. around dynamic buffers). If this was added due to false positives, consider scoping via NOLINT comments or path filters instead of blanket disable.

Provide context (e.g., specific false positives) or consider narrowing with --config-file or per-file excludes.

deps/curl/lib/sendf.h (1)

53-53: CLIENTWRITE_0LEN may trip Curl_client_write() debug asserts

New flag is sensible. However, Curl_client_write() currently only allows BODY/INFO combined with EOS in its debug asserts. Using CLIENTWRITE_0LEN with BODY/INFO would assert in debug builds. Either:

  • allow CLIENTWRITE_0LEN in those asserts, or
  • document it as HEADER‑only.

Suggested change outside this file (sendf.c):

--- a/deps/curl/lib/sendf.c
+++ b/deps/curl/lib/sendf.c
@@ -78,10 +78,10 @@ CURLcode Curl_client_write(struct Curl_easy *data,
-  DEBUGASSERT(!(type & CLIENTWRITE_BODY) ||
-              ((type & ~(CLIENTWRITE_BODY|CLIENTWRITE_EOS)) == 0));
+  DEBUGASSERT(!(type & CLIENTWRITE_BODY) ||
+              ((type & ~(CLIENTWRITE_BODY|CLIENTWRITE_EOS|CLIENTWRITE_0LEN)) == 0));
   /* INFO is only INFO (with optional EOS) */
-  DEBUGASSERT(!(type & CLIENTWRITE_INFO) ||
-              ((type & ~(CLIENTWRITE_INFO|CLIENTWRITE_EOS)) == 0));
+  DEBUGASSERT(!(type & CLIENTWRITE_INFO) ||
+              ((type & ~(CLIENTWRITE_INFO|CLIENTWRITE_EOS|CLIENTWRITE_0LEN)) == 0));
deps/curl/include/curl/typecheck-gcc.h (1)

224-237: New multi option predicates (cb-data, char) — sanity check deprecated opts**

Includes PIPELINING_*_BL; confirm these exist in bundled 8.16.0 headers for consistency.

deps/curl/lib/curl_trc.c (1)

301-314: Unit mismatch in timeout trace (ns vs timediff_us)

Message says “ns” but uses curlx_timediff_us(). Either change label to “us” or switch to a nanosecond diff helper.

-      CURL_TRC_M(data, "[TIMEOUT] %s expires in %" FMT_TIMEDIFF_T "ns",
+      CURL_TRC_M(data, "[TIMEOUT] %s expires in %" FMT_TIMEDIFF_T "us",
                  CURL_TIMER_NAME(n->eid),
                  curlx_timediff_us(n->time, now));
deps/curl/lib/pingpong.h (1)

150-153: Add pollset contract docs; callers migrated

No remaining Curl_pp_getsock references found; callers now use Curl_pp_pollset. Add a short contract comment in deps/curl/lib/pingpong.h next to the Curl_pp_pollset declaration:

/* Contract:

    • Populate ps with the sockets/events the pingpong state machine needs next.
    • Must be non-blocking; return CURLE_OK on success.
    • Return CURLE_AGAIN if no sockets yet (e.g., command queued), CURLE_ABORTED_BY_CALLBACK
  • if a callback aborted, or another CURLcode on hard errors.
    • ps may be set to "out-only" when write flush is pending.
      */
deps/curl/lib/multiif.h (2)

75-77: Mark Curl_multi_pollset as WARN_UNUSED_RESULT

Callers should not ignore failures. Align with Curl_update_timer’s annotation.

-CURLMcode Curl_multi_pollset(struct Curl_easy *data,
-                             struct easy_pollset *ps,
-                             const char *caller);
+CURLMcode Curl_multi_pollset(struct Curl_easy *data,
+                             struct easy_pollset *ps,
+                             const char *caller) WARN_UNUSED_RESULT;

165-166: API addition looks good — no code references remain

Clear counterpart to Curl_multi_mark_dirty; naming and placement are consistent. Search found no calls to Curl_multi_getsock in deps/curl; only remaining mention is doc/internals/MULTI-EV.md:50 (Curl_multi_getsock()) — update or remove that doc reference.

deps/curl/lib/cf-haproxy.c (2)

69-77: Typo: cf_haproxy_date_out_set → cf_haproxy_data_out_set

Rename for clarity and consistency.

-static CURLcode cf_haproxy_date_out_set(struct Curl_cfilter*cf,
+static CURLcode cf_haproxy_data_out_set(struct Curl_cfilter*cf,
                                         struct Curl_easy *data)
@@
-    result = cf_haproxy_date_out_set(cf, data);
+    result = cf_haproxy_data_out_set(cf, data);

Also applies to: 127-129


182-193: Defensive check for invalid socket in adjust_pollset

Avoid setting poll on CURL_SOCKET_BAD.

 static CURLcode cf_haproxy_adjust_pollset(struct Curl_cfilter *cf,
                                           struct Curl_easy *data,
                                           struct easy_pollset *ps)
 {
   if(cf->next->connected && !cf->connected) {
     /* If we are not connected, but the filter "below" is
      * and not waiting on something, we are sending. */
-    return Curl_pollset_set_out_only(
-      data, ps, Curl_conn_cf_get_socket(cf, data));
+    curl_socket_t s = Curl_conn_cf_get_socket(cf, data);
+    if(s == CURL_SOCKET_BAD)
+      return CURLE_OK;
+    return Curl_pollset_set_out_only(data, ps, s);
   }
   return CURLE_OK;
 }
deps/curl/lib/cookie.h (1)

115-115: Document Curl_secure_context semantics

Add brief doc on what constitutes “secure” (TLS, proxy TLS, Unix sockets?) for consistent callers.

deps/curl/lib/asyn-base.c (1)

88-89: Consider using a named constant for the socket array size.

The hard-coded value of 16 sockets should be defined as a constant for better maintainability.

+#define ARES_MAX_SOCKETS 16
+
 CURLcode Curl_ares_pollset(struct Curl_easy *data,
                            ares_channel channel,
                            struct easy_pollset *ps)
 {
   struct timeval maxtime = { CURL_TIMEOUT_RESOLVE, 0 };
   struct timeval timebuf;
-  curl_socket_t sockets[16];  /* ARES documented limit */
+  curl_socket_t sockets[ARES_MAX_SOCKETS];  /* ARES documented limit */
deps/curl/lib/conncache.c (1)

853-860: Remove misleading (void)data in cpool_mark_stale/reap helpers.

cpool_reap_no_reuse marks data as unused but uses it later. Drop the cast to avoid confusion.

Apply:

 static int cpool_mark_stale(struct Curl_easy *data,
                             struct connectdata *conn, void *param)
 {
-  (void)data;
   (void)param;
   conn->bits.no_reuse = TRUE;
   return 0;
 }

 static int cpool_reap_no_reuse(struct Curl_easy *data,
                                struct connectdata *conn, void *param)
 {
-  (void)data;
   (void)param;
   if(!CONN_INUSE(conn) && conn->bits.no_reuse) {
     Curl_conn_terminate(data, conn, FALSE);
     return 1;
   }
   return 0; /* continue iteration */
 }
deps/curl/lib/mqtt.c (1)

110-112: Pollset migration LGTM; add bad-socket guard.

Move from getsock→pollset is correct. Add a quick check for an invalid socket before registering.

Apply:

 static CURLcode mqtt_pollset(struct Curl_easy *data,
                              struct easy_pollset *ps)
 {
-  return Curl_pollset_add_in(data, ps, data->conn->sock[FIRSTSOCKET]);
+  curl_socket_t s = data->conn->sock[FIRSTSOCKET];
+  if(s == CURL_SOCKET_BAD)
+    return CURLE_OK;
+  return Curl_pollset_add_in(data, ps, s);
 }

Also applies to: 129-132, 216-221

deps/curl/include/curl/multi.h (1)

471-479: Typo in comment (CULRM_OK).

Change to CURLM_OK.

Apply:

- * Returns: CULRM_OK or error when value could not be obtained.
+ * Returns: CURLM_OK or error when value could not be obtained.
deps/curl/lib/md4.c (1)

55-60: mbedTLS config include may not exist on all builds.

Use MBEDTLS_CONFIG_FILE or fallback to config.h to improve portability.

Apply:

-#include <mbedtls/mbedtls_config.h>
+#ifdef MBEDTLS_CONFIG_FILE
+#include MBEDTLS_CONFIG_FILE
+#else
+#include <mbedtls/config.h>
+#endif
deps/curl/lib/cf-https-connect.c (2)

430-448: Avoid short‑circuiting pollset updates after the first failure

Early‑exit means one failing baller prevents the remaining active ballers from registering their fds, which can stall the happy‑eyeballs race. Prefer continuing to adjust the pollset while remembering the first error.

Apply:

 static CURLcode cf_hc_adjust_pollset(struct Curl_cfilter *cf,
                                      struct Curl_easy *data,
                                      struct easy_pollset *ps)
 {
-  CURLcode result = CURLE_OK;
+  CURLcode result = CURLE_OK;
+  CURLcode rc = CURLE_OK;
   if(!cf->connected) {
     struct cf_hc_ctx *ctx = cf->ctx;
     size_t i;
 
-    for(i = 0; (i < ctx->baller_count) && !result; i++) {
+    for(i = 0; i < ctx->baller_count; i++) {
       struct cf_hc_baller *b = &ctx->ballers[i];
       if(!cf_hc_baller_is_active(b))
         continue;
-      result = Curl_conn_cf_adjust_pollset(b->cf, data, ps);
+      rc = Curl_conn_cf_adjust_pollset(b->cf, data, ps);
+      if(!result && rc)
+        result = rc; /* record first error, keep going */
     }
     CURL_TRC_CF(data, cf, "adjust_pollset -> %d, %d socks", result, ps->n);
   }
   return result;
 }

689-697: Unify Curl_conn_may_http3 checks for readability

Two code paths check Curl_conn_may_http3 differently (one inline in condition, one storing to result). Consider a consistent pattern (store to a local CURLcode and branch) for clarity.

Also applies to: 716-727

deps/curl/lib/pingpong.c (1)

400-407: Guard against invalid socket when updating pollset

If FIRSTSOCKET is CURL_SOCKET_BAD (early connect/teardown edge), Curl_pollset_change may still be called. Cheap guard avoids surprising errors.

Apply:

 CURLcode Curl_pp_pollset(struct Curl_easy *data,
                          struct pingpong *pp,
                          struct easy_pollset *ps)
 {
-  int flags = pp->sendleft ? CURL_POLL_OUT : CURL_POLL_IN;
-  return Curl_pollset_change(data, ps, data->conn->sock[FIRSTSOCKET],
-                             flags, 0);
+  int flags = pp->sendleft ? CURL_POLL_OUT : CURL_POLL_IN;
+  curl_socket_t s = data->conn->sock[FIRSTSOCKET];
+  if(s == CURL_SOCKET_BAD)
+    return CURLE_OK;
+  return Curl_pollset_change(data, ps, s, flags, 0);
 }
deps/curl/lib/connect.c (1)

439-441: Fix minor grammar in error message

“not support” → “not supported”.

-        failf(data, "haproxy protocol not support with SSL "
-              "encryption in place (QUIC?)");
+        failf(data, "haproxy protocol not supported with SSL "
+              "encryption in place (QUIC?)");
deps/curl/lib/imap.c (1)

1562-1569: Defensive check before pollset update

Mirror the pingpong guard to skip updating the pollset when the socket is invalid.

 static CURLcode imap_pollset(struct Curl_easy *data,
                              struct easy_pollset *ps)
 {
   struct imap_conn *imapc =
     Curl_conn_meta_get(data->conn, CURL_META_IMAP_CONN);
-  return imapc ? Curl_pp_pollset(data, &imapc->pp, ps) : CURLE_OK;
+  if(!imapc)
+    return CURLE_OK;
+  if(data->conn->sock[FIRSTSOCKET] == CURL_SOCKET_BAD)
+    return CURLE_OK;
+  return Curl_pp_pollset(data, &imapc->pp, ps);
 }
deps/curl/lib/select.c (6)

44-44: Guard curl_trc.h include for non-tracing builds.

If curl_trc.h is not present or compiled out in some configs, this unconditional include can break non‑tracing builds.

Proposed guard:

-#include "curl_trc.h"
+#if defined(DEBUGBUILD) || defined(CURL_ENABLE_TRACING)
+#include "curl_trc.h"
+#endif

522-529: Provide a destroy helper for symmetry.

You added create/init/cleanup but not destroy. Exposing a small wrapper avoids allocator mismatches and reduces call‑site errors.

Proposed addition (outside this hunk, colocate with create/cleanup):

void Curl_pollset_destroy(struct easy_pollset *ps)
{
  if(!ps) return;
  Curl_pollset_cleanup(ps);
  free(ps);
}

547-566: Clarify init/reset interplay in move to avoid double work.

After transferring ownership, you call Curl_pollset_init(from) which reallocates defaults and then calls reset (which again clears). Consider reset-only to save a memset of def_* twice.

Proposed tweak:

-    Curl_pollset_init(from);
+    /* from now owns only its def_*; just clear visible entries */
+    Curl_pollset_reset(from);

The else-branch can keep Curl_pollset_init(from) for explicit re-init if preferred.


571-643: Minor: (void)data contradicts later use in trace; tighten overflow guard.

  • (void)data precedes a CURL_TRC_M(data, ...) use. Drop the cast to avoid confusion.
  • new_count computation is good; consider documenting the overflow guard path returning CURLE_OUT_OF_MEMORY.
-  (void)data;
+  /* 'data' used for tracing */

656-694: OOM path returns -1 indistinguishable from syscall error.

Returning -1 on calloc failure mirrors Curl_poll()’s error contract but loses errno context. If feasible, set errno = ENOMEM before returning to aid diagnostics.

-  if(!pfds)
-    return -1;
+  if(!pfds) {
+    SET_ERRNO(ENOMEM);
+    return -1;
+  }

696-712: Harden pointer params.

Add DEBUGASSERTs for pwant_read/pwant_write to catch misuse in debug builds.

-  (void)data;
+  (void)data;
+#ifdef DEBUGBUILD
+  DEBUGASSERT(pwant_read);
+  DEBUGASSERT(pwant_write);
+#endif
deps/curl/lib/asyn.h (1)

74-83: Update comment to match new pollset API.

Docstring still describes “getsock”-style arrays and bitmask return. Align it with Curl_async_pollset(struct Curl_easy*, struct easy_pollset*).

-/* Curl_async_pollset()
- *
- * This function is called from the Curl_multi_pollset() function.  'sock' is a
- * pointer to an array to hold the file descriptors, with 'numsock' being the
- * size of that array (in number of entries). This function is supposed to
- * return bitmask indicating what file descriptors (referring to array indexes
- * in the 'sock' array) to wait for, read/write.
- */
+/* Curl_async_pollset()
+ *
+ * Called from Curl_multi_pollset(). Implementations append sockets and
+ * desired actions (CURL_POLL_IN/OUT) into the provided easy_pollset 'ps'.
+ * Returns a CURLcode; no bitmask or array index semantics remain.
+ */
deps/curl/lib/multi.c (3)

1141-1155: Avoid noisy “WARNING: no socket in pollset or timer” in non‑debug builds.

This infof() warns and asserts when expect_sockets==true but no fds/timeouts exist. In some edge transitions this can be transient and spam logs. Gate the message behind verbose or emit once per transfer.

-    infof(data, "WARNING: no socket in pollset or timer, transfer may stall!");
-    DEBUGASSERT(0);
+    if(CURL_TRC_M_is_verbose(data))
+      infof(data, "WARNING: no socket in pollset or timer, transfer may stall!");
+    DEBUGASSERT(0);

1888-1920: Rate‑limit transition: clear dirty before scheduling and document semantics.

mspeed_check() moves to MSTATE_RATELIMITING and expires EXPIRE_TOOFAST, then clears dirty. That’s correct; consider a brief comment that clearing dirty prevents immediate re‑runs until the timeout fires to avoid oscillation.


3754-3789: Public counter API: define “added” semantics and overflow behavior.

curl_multi_get_offt() exposes ADDED (xfers_total_ever). If adds that later fail (e.g., timer setup failure) are counted, callers may get surprises. Align with the fix above or clarify docs that ADDED counts attempted adds. Also ensure counters are 64‑bit long‑lived and won’t wrap in long‑running processes.

deps/curl/lib/rtsp.c (2)

118-122: Null‑check data->conn in rtsp_do_pollset.

Accesses data->conn->sock[FIRSTSOCKET] without a guard. Mirror the other pollset helpers and early‑return if conn is NULL.

 static CURLcode rtsp_do_pollset(struct Curl_easy *data,
                                 struct easy_pollset *ps)
 {
-  /* write mode */
-  return Curl_pollset_add_out(data, ps, data->conn->sock[FIRSTSOCKET]);
+  if(data->conn && data->conn->sock[FIRSTSOCKET] != CURL_SOCKET_BAD)
+    return Curl_pollset_add_out(data, ps, data->conn->sock[FIRSTSOCKET]);
+  return CURLE_OK;
 }

759-769: Guard RTP channel mask indexing.

idx is derived from an arbitrary byte and used to index rtp_channel_mask. Add a bounds check against sizeof(rtp_channel_mask) to avoid OOB reads with malformed interleaved data.

-      int idx = ((unsigned char)buf[0]) / 8;
+      int idx = ((unsigned char)buf[0]) / 8;
       int off = ((unsigned char)buf[0]) % 8;
       DEBUGASSERT(curlx_dyn_len(&rtspc->buf) == 1);
-      if(!(data->state.rtp_channel_mask[idx] & (1 << off))) {
+      if(idx < 0 || idx >= (int)sizeof(data->state.rtp_channel_mask) ||
+         !(data->state.rtp_channel_mask[idx] & (1 << off))) {
deps/curl/lib/cf-socket.c (1)

2113-2120: Don’t gate accept() on getsockname() success.

If getsockname() spuriously fails, we skip accept entirely. Try accept() regardless; use getsockname() only to (best‑effort) fill ‘add’, not to decide whether to accept.

-  if(!getsockname(ctx->sock, (struct sockaddr *) &add, &size)) {
-    size = sizeof(add);
-#ifdef HAVE_ACCEPT4
-    s_accepted = accept4(ctx->sock, (struct sockaddr *) &add, &size,
-                         SOCK_NONBLOCK | SOCK_CLOEXEC);
-#else
-    s_accepted = CURL_ACCEPT(ctx->sock, (struct sockaddr *) &add, &size);
-#endif
-  }
+  (void)getsockname(ctx->sock, (struct sockaddr *)&add, &size); /* best effort */
+  size = sizeof(add);
+#ifdef HAVE_ACCEPT4
+  s_accepted = accept4(ctx->sock, (struct sockaddr *)&add, &size,
+                       SOCK_NONBLOCK | SOCK_CLOEXEC);
+#else
+  s_accepted = CURL_ACCEPT(ctx->sock, (struct sockaddr *)&add, &size);
+#endif
deps/curl/lib/cf-ip-happy.c (3)

352-513: Happy Eyeballs runner: timeout scheduling looks correct; set explicit lower bound.

You clamp next_expire_ms to min(connect‑timeleft, attempt delay), but if happy_eyeballs_timeout is configured very low (0/negative), expire scheduling may hammer the loop. Consider enforcing a small floor (e.g., 1 ms) when >0 attempts are ongoing.

-    if(next_expire_ms <= 0) {
+    if(next_expire_ms <= 0) {
       failf(data, "Connection timeout after %" FMT_OFF_T " ms",
             curlx_timediff(now, data->progress.t_startsingle));
       return CURLE_OPERATION_TIMEDOUT;
     }
-    Curl_expire(data, next_expire_ms, EXPIRE_HAPPY_EYEBALLS);
+    Curl_expire(data, CURLMAX(next_expire_ms, 1), EXPIRE_HAPPY_EYEBALLS);

748-789: Winner installation is tidy; ensure chain ownership is clear.

You detach the winning cf from the attempt, install as cf->next, then clear and stop the EXPIRE_HAPPY_EYEBALLS timer. Good. Add a short comment that the winning sub‑filter must own the socket from here, and ballers’ remaining attempts are already freed in cf_ip_happy_ctx_clear().


930-951: Public insertion helper: validate transport and factory lookup is robust.

get_cf_create() lookup and CURLE_UNSUPPORTED_PROTOCOL on unknown transport is correct. Consider logging the chosen transport name for traceability.

deps/curl/lib/hostip.c (5)

183-186: Use consistent time type across the structure

The struct uses struct curltime now but timediff_t for age values. While functionally correct, consider whether all time-related fields should use the same representation for consistency.


202-211: Double negative entry aging may lead to premature eviction

Negative DNS entries age twice as fast (Line 206: age *= 2). This could cause legitimate negative entries to be evicted prematurely from the cache, potentially leading to increased DNS queries for non-existent domains.


219-234: Consider extracting cache size management logic

The cache pruning logic with dynamic timeout adjustment (Lines 282-294) is complex and could benefit from being extracted into a separate helper function for better testability and maintainability.


803-823: Consider rate limiting negative DNS cache entries

The store_negative_resolve function adds negative entries without any rate limiting. A malicious actor could potentially fill the DNS cache with negative entries by requesting many non-existent hostnames.


1643-1656: CURL_DNS_DELAY_MS: documented — add explicit upper bound and overflow guard in hostip.c

  • Documentation exists: curl-upstream/docs/libcurl/libcurl-env-dbg.md → CURL_DNS_DELAY_MS.
  • In deps/curl/lib/hostip.c (Curl_resolve_test_delay) the value is parsed with curlx_str_number(..., TIME_T_MAX) then passed to curlx_wait_ms((timediff_t)l); clamp/validate l to a sane maximum and ensure the cast to timediff_t cannot overflow to prevent excessive test delays or undefined behavior.
deps/curl/lib/asyn-thrdd.c (1)

231-284: Potential race condition in thread abort check

In getaddrinfo_thread, the do_abort flag is checked after acquiring the mutex (Line 233), but the actual DNS resolution happens outside the mutex protection. This could lead to unnecessary work if abort is signaled during resolution.

Consider checking the abort flag again after DNS resolution completes:

    rc = Curl_getaddrinfo_ex(addr_ctx->hostname, service,
                             &addr_ctx->hints, &addr_ctx->res);

+   Curl_mutex_acquire(&addr_ctx->mutx);
+   do_abort = addr_ctx->do_abort;
+   Curl_mutex_release(&addr_ctx->mutx);
+   
+   if(do_abort) {
+     if(addr_ctx->res) {
+       Curl_freeaddrinfo(addr_ctx->res);
+       addr_ctx->res = NULL;
+     }
+   }
+   else {
    if(rc) {
      addr_ctx->sock_error = SOCKERRNO ? SOCKERRNO : rc;
      if(addr_ctx->sock_error == 0)
        addr_ctx->sock_error = RESOLVER_ENOMEM;
    }
    else {
      Curl_addrinfo_set_port(addr_ctx->res, addr_ctx->port);
    }
+   }
deps/curl/lib/ftp.c (1)

4186-4198: Recursive slash counting implementation

The numof_slashes function counts slashes in a string. Consider using a simpler implementation:

-static size_t numof_slashes(const char *str)
-{
-  const char *slashPos;
-  size_t num = 0;
-  do {
-    slashPos = strchr(str, '/');
-    if(slashPos) {
-      ++num;
-      str = slashPos + 1;
-    }
-  } while(slashPos);
-  return num;
-}
+static size_t numof_slashes(const char *str)
+{
+  size_t num = 0;
+  while(*str) {
+    if(*str == '/')
+      num++;
+    str++;
+  }
+  return num;
+}
deps/curl/lib/curl_setup.h (1)

627-644: Safer SIZE_MAX/SSIZE_MAX fallbacks.

Literal constants risk width mismatches on exotic ABIs. Prefer portable derivations.

Suggested tweak:

-#if defined(SIZEOF_SIZE_T) && (SIZEOF_SIZE_T > 4)
-#define SIZE_MAX 18446744073709551615U
-#else
-#define SIZE_MAX 4294967295U
-#endif
+#ifndef SIZE_MAX
+#define SIZE_MAX ((size_t)~(size_t)0)
+#endif
@@
-#if defined(SIZEOF_SIZE_T) && (SIZEOF_SIZE_T > 4)
-#define SSIZE_MAX 9223372036854775807
-#else
-#define SSIZE_MAX 2147483647
-#endif
+#ifndef SSIZE_MAX
+#define SSIZE_MAX ((ssize_t)(SIZE_MAX >> 1))
+#endif
deps/curl/lib/cfilters.c (1)

1064-1078: Return 1 (not 0) when sockindex invalid to preserve default concurrency.

The function documents defaulting to non‑multiplexed “1”. Returning 0 may cause callers to treat it as “no capacity”.

Apply this diff:

-  if(!CONN_SOCK_IDX_VALID(sockindex))
-    return 0;
+  if(!CONN_SOCK_IDX_VALID(sockindex))
+    return 1;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between ae1497d and 858bae6.

📒 Files selected for processing (149)
  • deps/curl/.editorconfig (1 hunks)
  • deps/curl/include/Makefile.in (1 hunks)
  • deps/curl/include/curl/Makefile.in (1 hunks)
  • deps/curl/include/curl/curl.h (12 hunks)
  • deps/curl/include/curl/curlver.h (3 hunks)
  • deps/curl/include/curl/easy.h (2 hunks)
  • deps/curl/include/curl/header.h (1 hunks)
  • deps/curl/include/curl/mprintf.h (3 hunks)
  • deps/curl/include/curl/multi.h (3 hunks)
  • deps/curl/include/curl/options.h (1 hunks)
  • deps/curl/include/curl/system.h (11 hunks)
  • deps/curl/include/curl/typecheck-gcc.h (8 hunks)
  • deps/curl/include/curl/urlapi.h (1 hunks)
  • deps/curl/include/curl/websockets.h (2 hunks)
  • deps/curl/lib/CMakeLists.txt (4 hunks)
  • deps/curl/lib/Makefile.am (3 hunks)
  • deps/curl/lib/Makefile.in (25 hunks)
  • deps/curl/lib/Makefile.inc (3 hunks)
  • deps/curl/lib/altsvc.c (2 hunks)
  • deps/curl/lib/asyn-ares.c (5 hunks)
  • deps/curl/lib/asyn-base.c (1 hunks)
  • deps/curl/lib/asyn-thrdd.c (18 hunks)
  • deps/curl/lib/asyn.h (6 hunks)
  • deps/curl/lib/bufq.c (4 hunks)
  • deps/curl/lib/cf-h1-proxy.c (2 hunks)
  • deps/curl/lib/cf-h2-proxy.c (10 hunks)
  • deps/curl/lib/cf-h2-proxy.h (1 hunks)
  • deps/curl/lib/cf-haproxy.c (3 hunks)
  • deps/curl/lib/cf-haproxy.h (1 hunks)
  • deps/curl/lib/cf-https-connect.c (5 hunks)
  • deps/curl/lib/cf-https-connect.h (2 hunks)
  • deps/curl/lib/cf-ip-happy.c (1 hunks)
  • deps/curl/lib/cf-ip-happy.h (1 hunks)
  • deps/curl/lib/cf-socket.c (16 hunks)
  • deps/curl/lib/cf-socket.h (0 hunks)
  • deps/curl/lib/cfilters.c (21 hunks)
  • deps/curl/lib/cfilters.h (9 hunks)
  • deps/curl/lib/config-os400.h (0 hunks)
  • deps/curl/lib/config-win32.h (1 hunks)
  • deps/curl/lib/conncache.c (3 hunks)
  • deps/curl/lib/conncache.h (1 hunks)
  • deps/curl/lib/connect.c (5 hunks)
  • deps/curl/lib/connect.h (0 hunks)
  • deps/curl/lib/content_encoding.c (6 hunks)
  • deps/curl/lib/cookie.c (14 hunks)
  • deps/curl/lib/cookie.h (1 hunks)
  • deps/curl/lib/cshutdn.c (3 hunks)
  • deps/curl/lib/curl_addrinfo.c (4 hunks)
  • deps/curl/lib/curl_config.h.cmake (0 hunks)
  • deps/curl/lib/curl_config.h.in (1 hunks)
  • deps/curl/lib/curl_fnmatch.c (1 hunks)
  • deps/curl/lib/curl_gethostname.c (1 hunks)
  • deps/curl/lib/curl_gssapi.c (1 hunks)
  • deps/curl/lib/curl_hmac.h (1 hunks)
  • deps/curl/lib/curl_md4.h (1 hunks)
  • deps/curl/lib/curl_md5.h (1 hunks)
  • deps/curl/lib/curl_mem_undef.h (1 hunks)
  • deps/curl/lib/curl_memory.h (0 hunks)
  • deps/curl/lib/curl_ntlm_core.c (11 hunks)
  • deps/curl/lib/curl_ntlm_core.h (2 hunks)
  • deps/curl/lib/curl_rtmp.c (9 hunks)
  • deps/curl/lib/curl_sasl.c (4 hunks)
  • deps/curl/lib/curl_setup.h (13 hunks)
  • deps/curl/lib/curl_setup_once.h (0 hunks)
  • deps/curl/lib/curl_sha256.h (1 hunks)
  • deps/curl/lib/curl_sha512_256.c (21 hunks)
  • deps/curl/lib/curl_sspi.c (1 hunks)
  • deps/curl/lib/curl_sspi.h (3 hunks)
  • deps/curl/lib/curl_threads.c (5 hunks)
  • deps/curl/lib/curl_threads.h (2 hunks)
  • deps/curl/lib/curl_trc.c (9 hunks)
  • deps/curl/lib/curl_trc.h (3 hunks)
  • deps/curl/lib/curlx/base64.c (0 hunks)
  • deps/curl/lib/curlx/dynbuf.c (4 hunks)
  • deps/curl/lib/curlx/dynbuf.h (1 hunks)
  • deps/curl/lib/curlx/inet_ntop.c (1 hunks)
  • deps/curl/lib/curlx/inet_ntop.h (1 hunks)
  • deps/curl/lib/curlx/inet_pton.h (1 hunks)
  • deps/curl/lib/curlx/multibyte.h (1 hunks)
  • deps/curl/lib/curlx/nonblock.c (1 hunks)
  • deps/curl/lib/curlx/strparse.c (1 hunks)
  • deps/curl/lib/curlx/timeval.c (3 hunks)
  • deps/curl/lib/curlx/wait.c (1 hunks)
  • deps/curl/lib/curlx/warnless.c (3 hunks)
  • deps/curl/lib/cw-out.c (8 hunks)
  • deps/curl/lib/dict.c (4 hunks)
  • deps/curl/lib/dllmain.c (1 hunks)
  • deps/curl/lib/doh.c (13 hunks)
  • deps/curl/lib/doh.h (1 hunks)
  • deps/curl/lib/easy.c (12 hunks)
  • deps/curl/lib/easy_lock.h (1 hunks)
  • deps/curl/lib/easygetopt.c (1 hunks)
  • deps/curl/lib/file.c (6 hunks)
  • deps/curl/lib/fileinfo.c (2 hunks)
  • deps/curl/lib/fopen.c (1 hunks)
  • deps/curl/lib/formdata.c (2 hunks)
  • deps/curl/lib/ftp.c (36 hunks)
  • deps/curl/lib/ftp.h (2 hunks)
  • deps/curl/lib/gopher.c (3 hunks)
  • deps/curl/lib/hmac.c (2 hunks)
  • deps/curl/lib/hostip.c (14 hunks)
  • deps/curl/lib/hostip.h (5 hunks)
  • deps/curl/lib/hostip4.c (4 hunks)
  • deps/curl/lib/hostip6.c (2 hunks)
  • deps/curl/lib/hsts.c (2 hunks)
  • deps/curl/lib/http.c (32 hunks)
  • deps/curl/lib/http.h (3 hunks)
  • deps/curl/lib/http2.c (14 hunks)
  • deps/curl/lib/http_aws_sigv4.c (2 hunks)
  • deps/curl/lib/http_chunks.c (4 hunks)
  • deps/curl/lib/http_digest.c (2 hunks)
  • deps/curl/lib/http_ntlm.c (1 hunks)
  • deps/curl/lib/http_proxy.c (2 hunks)
  • deps/curl/lib/httpsrr.h (1 hunks)
  • deps/curl/lib/idn.c (3 hunks)
  • deps/curl/lib/if2ip.c (3 hunks)
  • deps/curl/lib/imap.c (17 hunks)
  • deps/curl/lib/krb5.c (4 hunks)
  • deps/curl/lib/ldap.c (22 hunks)
  • deps/curl/lib/libcurl.def (2 hunks)
  • deps/curl/lib/md4.c (6 hunks)
  • deps/curl/lib/md5.c (5 hunks)
  • deps/curl/lib/memdebug.c (2 hunks)
  • deps/curl/lib/memdebug.h (1 hunks)
  • deps/curl/lib/mime.c (12 hunks)
  • deps/curl/lib/mqtt.c (3 hunks)
  • deps/curl/lib/multi.c (30 hunks)
  • deps/curl/lib/multi_ev.c (9 hunks)
  • deps/curl/lib/multihandle.h (2 hunks)
  • deps/curl/lib/multiif.h (2 hunks)
  • deps/curl/lib/netrc.c (1 hunks)
  • deps/curl/lib/netrc.h (1 hunks)
  • deps/curl/lib/noproxy.c (3 hunks)
  • deps/curl/lib/openldap.c (5 hunks)
  • deps/curl/lib/optiontable.pl (3 hunks)
  • deps/curl/lib/parsedate.c (3 hunks)
  • deps/curl/lib/parsedate.h (1 hunks)
  • deps/curl/lib/pingpong.c (1 hunks)
  • deps/curl/lib/pingpong.h (1 hunks)
  • deps/curl/lib/pop3.c (13 hunks)
  • deps/curl/lib/progress.c (2 hunks)
  • deps/curl/lib/rand.c (4 hunks)
  • deps/curl/lib/request.c (4 hunks)
  • deps/curl/lib/request.h (0 hunks)
  • deps/curl/lib/rtsp.c (9 hunks)
  • deps/curl/lib/select.c (4 hunks)
  • deps/curl/lib/select.h (1 hunks)
  • deps/curl/lib/sendf.c (15 hunks)
  • deps/curl/lib/sendf.h (5 hunks)
💤 Files with no reviewable changes (8)
  • deps/curl/lib/request.h
  • deps/curl/lib/cf-socket.h
  • deps/curl/lib/config-os400.h
  • deps/curl/lib/curl_config.h.cmake
  • deps/curl/lib/connect.h
  • deps/curl/lib/curl_memory.h
  • deps/curl/lib/curlx/base64.c
  • deps/curl/lib/curl_setup_once.h
👮 Files not reviewed due to content moderation or server errors (1)
  • deps/curl/lib/Makefile.in
🧰 Additional context used
🧬 Code graph analysis (6)
deps/curl/lib/http.h (1)
deps/curl/lib/http.c (2)
  • Curl_http_method (1810-1852)
  • Curl_http_do_pollset (1519-1524)
deps/curl/lib/http_proxy.c (1)
deps/curl/lib/cf-h2-proxy.c (1)
  • Curl_cf_h2_proxy_insert_after (1594-1617)
deps/curl/lib/cookie.c (1)
deps/curl/lib/hostip.c (1)
  • Curl_host_is_ipnum (729-742)
deps/curl/lib/http.c (2)
deps/curl/lib/cookie.c (2)
  • Curl_cookie_getlist (1311-1407)
  • Curl_secure_context (1292-1298)
deps/curl/lib/http2.c (3)
  • Curl_http2_switch (2886-2906)
  • Curl_http2_may_switch (2868-2884)
  • Curl_http2_request_upgrade (1799-1835)
deps/curl/lib/multi.c (1)
deps/curl/lib/hostip.c (2)
  • Curl_resolv_pollset (1569-1585)
  • Curl_dnscache_clear (299-307)
deps/curl/lib/easy.c (1)
deps/curl/lib/multi.c (2)
  • Curl_multi_mark_dirty (3973-3977)
  • Curl_update_timer (3396-3462)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: test-tarball-linux
  • GitHub Check: lint-js-and-md
  • GitHub Check: test-linux (ubuntu-24.04-arm)
  • GitHub Check: test-linux (ubuntu-24.04)
  • GitHub Check: test-macOS

Comment on lines +398 to 401
/* network has changed, adjust caches/connection reuse */
CURLOPT(CURLMOPT_NETWORK_CHANGED, CURLOPTTYPE_LONG, 17),

CURLMOPT_LASTENTRY /* the last unused */
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Duplicate bit value for CLEAR_DNS — breaks flag semantics.

Both CLEAR_CONNS and CLEAR_DNS are (1L<<0). Assign a distinct bit and fix the comment.

Apply:

 /* - CURLMNWC_CLEAR_CONNS tells libcurl to prevent further reuse of existing
    connections. Connections that are idle will be closed. Ongoing transfers
    will continue with the connection they have. */
 #define CURLMNWC_CLEAR_CONNS (1L<<0)

-/* - CURLMNWC_CLEAR_DNS tells libcurl to prevent further reuse of existing
-   connections. Connections that are idle will be closed. Ongoing transfers
-   will continue with the connection they have. */
-#define CURLMNWC_CLEAR_DNS (1L<<0)
+/* - CURLMNWC_CLEAR_DNS tells libcurl to clear DNS-related caches (hosts,
+   Happy Eyeballs hints, etc.). Does not force-close active connections. */
+#define CURLMNWC_CLEAR_DNS   (1L<<1)

Also applies to: 404-415

🤖 Prompt for AI Agents
In deps/curl/include/curl/multi.h around lines 398-401 (and also review lines
404-415), two flags CLEAR_CONNS and CLEAR_DNS were assigned the same bit value
(1L<<0) breaking flag semantics; change CLEAR_DNS to use a distinct bit (e.g.,
1L<<1 or the next free bit), update any related enum/comment to reflect the
correct bit value and purpose, and ensure any masks or usage elsewhere in this
range are adjusted to use the new bit so flags are unique and the comment
accurately describes the bits.

Comment on lines +506 to +523
for(i = 0; i < ps.n; i++) {
#ifdef __DJGPP__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Warith-conversion"
#endif
if(ps.actions[i] & CURL_POLL_IN)
FD_SET(ps.sockets[i], read_fd_set);
if(ps.actions[i] & CURL_POLL_OUT)
FD_SET(ps.sockets[i], write_fd_set);
#if defined(__DJGPP__)
#ifdef __DJGPP__
#pragma GCC diagnostic pop
#endif
if((ps.actions[i] & (CURL_POLL_OUT | CURL_POLL_IN)) &&
((int)ps.sockets[i] > *maxfd))
*maxfd = (int)ps.sockets[i];
}
}
Curl_pollset_cleanup(&ps);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

🧩 Analysis chain

Fix unintended behavior change from ps.num to ps.n.

The loop condition changed from ps.num to ps.n, but these appear to be different fields. The struct easy_pollset likely has both n (current count) and num (capacity). Using ps.n here could cause the loop to terminate early if the pollset isn't fully populated.

Please verify the correct field to use:

-      for(i = 0; i < ps.n; i++) {
+      for(i = 0; i < ps.num; i++) {

🏁 Script executed:

#!/bin/bash
# Find the definition of struct easy_pollset to understand the fields
ast-grep --pattern 'struct easy_pollset {
  $$$
}'

# Check other usages of easy_pollset to understand the pattern
rg -n "easy_pollset" --type=c -A 3 -B 3 | head -50

Length of output: 2752


Replace ps.n with ps.num in deps/curl/lib/cshutdn.c

struct easy_pollset uses the field named "num" for the current count (other code — e.g. lib/cfilters.c — uses ps->num); using ps.n here will truncate the loop and change behavior.
Location: deps/curl/lib/cshutdn.c (around lines 506–523).

-      for(i = 0; i < ps.n; i++) {
+      for(i = 0; i < ps.num; i++) {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
for(i = 0; i < ps.n; i++) {
#ifdef __DJGPP__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Warith-conversion"
#endif
if(ps.actions[i] & CURL_POLL_IN)
FD_SET(ps.sockets[i], read_fd_set);
if(ps.actions[i] & CURL_POLL_OUT)
FD_SET(ps.sockets[i], write_fd_set);
#if defined(__DJGPP__)
#ifdef __DJGPP__
#pragma GCC diagnostic pop
#endif
if((ps.actions[i] & (CURL_POLL_OUT | CURL_POLL_IN)) &&
((int)ps.sockets[i] > *maxfd))
*maxfd = (int)ps.sockets[i];
}
}
Curl_pollset_cleanup(&ps);
for(i = 0; i < ps.num; i++) {
#ifdef __DJGPP__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Warith-conversion"
#endif
if(ps.actions[i] & CURL_POLL_IN)
FD_SET(ps.sockets[i], read_fd_set);
if(ps.actions[i] & CURL_POLL_OUT)
FD_SET(ps.sockets[i], write_fd_set);
#ifdef __DJGPP__
#pragma GCC diagnostic pop
#endif
if((ps.actions[i] & (CURL_POLL_OUT | CURL_POLL_IN)) &&
((int)ps.sockets[i] > *maxfd))
*maxfd = (int)ps.sockets[i];
}
}
Curl_pollset_cleanup(&ps);
🤖 Prompt for AI Agents
In deps/curl/lib/cshutdn.c around lines 506 to 523, the loop uses ps.n which is
the wrong field name; replace ps.n with ps.num throughout the loop so it
iterates over the actual count field of struct easy_pollset (update the for loop
and any other occurrences in this block), rebuild to ensure no warnings or
behavior changes, and run tests to confirm semantics match other uses (e.g.,
lib/cfilters.c) that reference ps->num.

Comment on lines 301 to 303
#ifndef SEC_E_POLICY_NLTM_ONLY
# define SEC_E_POLICY_NLTM_ONLY ((HRESULT)0x8009035FL)
#define SEC_E_POLICY_NLTM_ONLY ((HRESULT)0x8009035FL)
#endif
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

🧩 Analysis chain

Typo breaks the intended fallback: SEC_E_POLICY_NLTM_ONLY → SEC_E_POLICY_NTLM_ONLY.
The macro name is misspelled in both the #ifndef and #define, so the correct symbol remains undefined and the wrong one gets introduced.

Apply this diff:

-#ifndef SEC_E_POLICY_NLTM_ONLY
-#define SEC_E_POLICY_NLTM_ONLY                ((HRESULT)0x8009035FL)
+#ifndef SEC_E_POLICY_NTLM_ONLY
+#define SEC_E_POLICY_NTLM_ONLY                ((HRESULT)0x8009035FL)

Run to ensure no residual uses of the misspelled symbol and that callers use the correct one:


🏁 Script executed:

#!/bin/bash
rg -nP -C2 $'\bSEC_E_POLICY_NTLM_ONLY\b|\bSEC_E_POLICY_NLTM_ONLY\b'

Length of output: 0


🏁 Script executed:

#!/bin/bash
set -euo pipefail
echo "PWD: $(pwd)"
echo "Check ripgrep availability:"
rg --version 2>/dev/null || echo "rg not available"
echo
echo "Searching repo for both macros (NTLM / NLTM):"
rg -n --hidden --no-ignore -S --color=never '\bSEC_E_POLICY_NTLM_ONLY\b' || true
rg -n --hidden --no-ignore -S --color=never '\bSEC_E_POLICY_NLTM_ONLY\b' || true
echo
echo "Fallback grep (recursive, exclude .git):"
grep -RIn --exclude-dir=.git -E 'SEC_E_POLICY_NTLM_ONLY|SEC_E_POLICY_NLTM_ONLY' . || true
echo
echo "Show snippet around the macro in deps/curl/lib/curl_sspi.h (if present):"
sed -n '288,308p' deps/curl/lib/curl_sspi.h || echo "deps/curl/lib/curl_sspi.h not found"

Length of output: 1935


Fix macro typo and update all callers: SEC_E_POLICY_NLTM_ONLY → SEC_E_POLICY_NTLM_ONLY

The header macro is misspelled and is referenced in the tree — update the define and replace all usages found at:

  • deps/curl/lib/curl_sspi.h:301-302
  • deps/curl/lib/strerror.c:941
  • curl-upstream/lib/curl_sspi.h:301-302
  • curl-upstream/lib/strerror.c:941

Apply this header change and update callers accordingly:

-#ifndef SEC_E_POLICY_NLTM_ONLY
-#define SEC_E_POLICY_NLTM_ONLY                ((HRESULT)0x8009035FL)
+#ifndef SEC_E_POLICY_NTLM_ONLY
+#define SEC_E_POLICY_NTLM_ONLY                ((HRESULT)0x8009035FL)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
#ifndef SEC_E_POLICY_NLTM_ONLY
# define SEC_E_POLICY_NLTM_ONLY ((HRESULT)0x8009035FL)
#define SEC_E_POLICY_NLTM_ONLY ((HRESULT)0x8009035FL)
#endif
#ifndef SEC_E_POLICY_NTLM_ONLY
#define SEC_E_POLICY_NTLM_ONLY ((HRESULT)0x8009035FL)
#endif
🤖 Prompt for AI Agents
In deps/curl/lib/curl_sspi.h around lines 301-303 the macro name is misspelled
SEC_E_POLICY_NLTM_ONLY; change the define to SEC_E_POLICY_NTLM_ONLY and update
all callers to use the corrected macro name. Edit deps/curl/lib/strerror.c
(around line 941), curl-upstream/lib/curl_sspi.h (around lines 301-302) and
curl-upstream/lib/strerror.c (around line 941) to replace any occurrences of
SEC_E_POLICY_NLTM_ONLY with SEC_E_POLICY_NTLM_ONLY, verify includes still
resolve, and run a build/check to ensure no remaining references to the old
macro remain.

Comment on lines 295 to 306
CURLcode result = CURLE_OK;

if(curlx_dyn_len(&cwbuf->b)) {
if(curlx_dyn_len(&cwbuf->b) || (cwbuf->type == CW_OUT_BODY_0LEN)) {
size_t consumed;

result = cw_out_ptr_flush(ctx, data, cwbuf->type, flush_all,
curlx_dyn_ptr(&cwbuf->b),
curlx_dyn_len(&cwbuf->b),
&consumed);
if(result)
if(result && (result != CURLE_AGAIN))
return result;

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Bug: CURLE_AGAIN from buffered flush bubbles up as an error

cw_out_buf_flush may return CURLE_AGAIN, which cw_out_flush_chain propagates; callers (cw_out_do_write, cw_out_flush) then treat it as an error and set ctx->errored, dropping buffers and breaking pause/resume.

Handle CURLE_AGAIN like the direct write path (non‑error, keep buffers).

Apply these diffs:

--- a/deps/curl/lib/cw-out.c
+++ b/deps/curl/lib/cw-out.c
@@ -404,9 +404,11 @@ static CURLcode cw_out_do_write(struct cw_out_ctx *ctx,
-    result = cw_out_flush_chain(ctx, data, &ctx->buf, flush_all);
-    if(result)
-      goto out;
+    result = cw_out_flush_chain(ctx, data, &ctx->buf, flush_all);
+    if(result && (result != CURLE_AGAIN))
+      goto out;
+    /* treat PAUSE as non-error; keep buffered data */
+    result = CURLE_OK;
@@ -488,12 +490,14 @@ static CURLcode cw_out_flush(struct Curl_easy *data,
-  result = cw_out_flush_chain(ctx, data, &ctx->buf, flush_all);
-  if(result) {
-    ctx->errored = TRUE;
-    cw_out_bufs_free(ctx);
-    return result;
-  }
-  return result;
+  result = cw_out_flush_chain(ctx, data, &ctx->buf, flush_all);
+  if(result && (result != CURLE_AGAIN)) {
+    ctx->errored = TRUE;
+    cw_out_bufs_free(ctx);
+    return result;
+  }
+  /* OK or PAUSE -> not an error */
+  return CURLE_OK;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
CURLcode result = CURLE_OK;
if(curlx_dyn_len(&cwbuf->b)) {
if(curlx_dyn_len(&cwbuf->b) || (cwbuf->type == CW_OUT_BODY_0LEN)) {
size_t consumed;
result = cw_out_ptr_flush(ctx, data, cwbuf->type, flush_all,
curlx_dyn_ptr(&cwbuf->b),
curlx_dyn_len(&cwbuf->b),
&consumed);
if(result)
if(result && (result != CURLE_AGAIN))
return result;
result = cw_out_flush_chain(ctx, data, &ctx->buf, flush_all);
if(result && (result != CURLE_AGAIN))
goto out;
/* treat PAUSE as non-error; keep buffered data */
result = CURLE_OK;
Suggested change
CURLcode result = CURLE_OK;
if(curlx_dyn_len(&cwbuf->b)) {
if(curlx_dyn_len(&cwbuf->b) || (cwbuf->type == CW_OUT_BODY_0LEN)) {
size_t consumed;
result = cw_out_ptr_flush(ctx, data, cwbuf->type, flush_all,
curlx_dyn_ptr(&cwbuf->b),
curlx_dyn_len(&cwbuf->b),
&consumed);
if(result)
if(result && (result != CURLE_AGAIN))
return result;
result = cw_out_flush_chain(ctx, data, &ctx->buf, flush_all);
if(result && (result != CURLE_AGAIN)) {
ctx->errored = TRUE;
cw_out_bufs_free(ctx);
return result;
}
/* OK or PAUSE -> not an error */
return CURLE_OK;

Comment on lines +76 to 78
if(hashparams->hinit(ctxt->hashctxt1))
return NULL;
hashparams->hupdate(ctxt->hashctxt1, key, keylen);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Free ctxt on early-return to avoid a leak.

If hinit fails here, ctxt is leaked.

Apply this diff:

-    if(hashparams->hinit(ctxt->hashctxt1))
-      return NULL;
+    if(hashparams->hinit(ctxt->hashctxt1)) {
+      free(ctxt);
+      return NULL;
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if(hashparams->hinit(ctxt->hashctxt1))
return NULL;
hashparams->hupdate(ctxt->hashctxt1, key, keylen);
if(hashparams->hinit(ctxt->hashctxt1)) {
free(ctxt);
return NULL;
}
hashparams->hupdate(ctxt->hashctxt1, key, keylen);
🤖 Prompt for AI Agents
In deps/curl/lib/hmac.c around lines 76 to 78, the function returns NULL when
hashparams->hinit(ctxt->hashctxt1) fails but does not free the allocated ctxt,
leaking memory; fix by freeing ctxt (and any of its owned allocations if
applicable) before returning NULL so the early-return cleans up allocated
resources and then return NULL as before.

Comment on lines +395 to +400
case CF_QUERY_ALPN_NEGOTIATED: {
const char **palpn = pres2;
DEBUGASSERT(palpn);
*palpn = NULL;
return CURLE_OK;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

CF_QUERY_ALPN_NEGOTIATED should forward downstream instead of returning NULL.

Current code returns CURLE_OK with NULL, shadowing a negotiated ALPN from lower filters. Forward the query to cf->next and only default to unknown when there is no next.

Apply:

   case CF_QUERY_ALPN_NEGOTIATED: {
-    const char **palpn = pres2;
-    DEBUGASSERT(palpn);
-    *palpn = NULL;
-    return CURLE_OK;
+    return cf->next ?
+      cf->next->cft->query(cf->next, data, query, pres1, pres2) :
+      CURLE_UNKNOWN_OPTION;
   }
🤖 Prompt for AI Agents
In deps/curl/lib/http_proxy.c around lines 395-400, the CF_QUERY_ALPN_NEGOTIATED
case currently forces *palpn = NULL and returns CURLE_OK, which hides any ALPN
negotiated by downstream filters; change it to forward the
CF_QUERY_ALPN_NEGOTIATED query to the next filter when cf->next is present (by
invoking the next filter's query handler with the same parameters) and return
its result, and only set *palpn = NULL and return CURLE_OK when there is no next
filter to consult.

Comment on lines 495 to +498
/* add the easy handle to the process set */
Curl_uint_bset_add(&multi->process, data->mid);
++multi->xfers_alive;
++multi->xfers_total_ever;

Curl_cpool_xfer_init(data);
multi_warn_debug(multi, data);

/* Make sure the new handle will run */
Curl_multi_mark_dirty(data);
/* Necessary in event based processing, where dirty handles trigger
* a timeout callback invocation. */
rc = Curl_update_timer(multi);
if(rc) {
data->multi = NULL; /* not anymore */
Curl_uint_tbl_remove(&multi->xfers, data->mid);
data->mid = UINT_MAX;
return rc;
}

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix rollback when Curl_update_timer() fails in curl_multi_add_handle.

If Curl_update_timer() returns non‑zero, we detach the easy and remove it from xfers, but:

  • multi->xfers_alive is not decremented (it was incremented at Line 481).
  • The easy’s bits remain set in multi->process and multi->dirty.
  • xfers_total_ever was already incremented at Line 482, so CURLMINFO_XFERS_ADDED will overcount failed adds.

This leaves inconsistent counters/bitsets and can cause spurious scheduling and incorrect metrics. Either (a) move the increments after the successful timer update, or (b) fully roll back on error.

Apply one of the following diffs.

Option A (preferred: increment only after success):

@@
-  Curl_uint_bset_add(&multi->process, data->mid);
-  ++multi->xfers_alive;
-  ++multi->xfers_total_ever;
+  Curl_uint_bset_add(&multi->process, data->mid);
+  /* xfers_alive / xfers_total_ever are updated after timer install succeeds */
@@
-  rc = Curl_update_timer(multi);
+  rc = Curl_update_timer(multi);
   if(rc) {
     data->multi = NULL; /* not anymore */
-    Curl_uint_tbl_remove(&multi->xfers, data->mid);
-    data->mid = UINT_MAX;
+    {
+      unsigned int mid = data->mid;
+      Curl_uint_tbl_remove(&multi->xfers, mid);
+      Curl_uint_bset_remove(&multi->process, mid);
+      Curl_uint_bset_remove(&multi->dirty, mid);
+      data->mid = UINT_MAX;
+    }
     return rc;
   }
+  ++multi->xfers_alive;
+  ++multi->xfers_total_ever;

Option B (complete rollback on error):

@@
-  rc = Curl_update_timer(multi);
+  rc = Curl_update_timer(multi);
   if(rc) {
-    data->multi = NULL; /* not anymore */
-    Curl_uint_tbl_remove(&multi->xfers, data->mid);
-    data->mid = UINT_MAX;
+    unsigned int mid = data->mid;
+    --multi->xfers_alive;
+    Curl_uint_bset_remove(&multi->process, mid);
+    Curl_uint_bset_remove(&multi->dirty, mid);
+    data->multi = NULL; /* not anymore */
+    Curl_uint_tbl_remove(&multi->xfers, mid);
+    data->mid = UINT_MAX;
+    /* Optionally also revert xfers_total_ever if you intend it to track only
+       successfully added transfers. */
     return rc;
   }

Comment on lines 90 to 95
unsigned int xfers_alive; /* amount of added transfers that have
not yet reached COMPLETE state */
curl_off_t xfers_total_ever; /* total of added transfers, ever. */
struct uint_tbl xfers; /* transfers added to this multi */
/* Each transfer's mid may be present in at most one of these */
struct uint_bset process; /* transfer being processed */
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

🧩 Analysis chain

New accounting field must be initialized and maintained.

Ensure xfers_total_ever is zeroed on multi init and incremented exactly once per added transfer (and never decremented).

Run:


🏁 Script executed:

#!/bin/bash
set -euo pipefail
echo "Where xfers_total_ever is initialized/updated:"
rg -n 'xfers_total_ever'
echo
echo "Check multi initialization:"
rg -nP '(multi|Curl_multi)_init|Curl_multi_handle|multi_new|multi_init'

Length of output: 15414


Initialize and maintain xfers_total_ever correctly

xfers_total_ever is declared in multihandle.h and currently incremented in curl_multi_add_handle (++multi->xfers_total_ever at deps/curl/lib/multi.c:482) and exposed via CURLMINFO_XFERS_ADDED (deps/curl/lib/multi.c:3783). The increment happens before the function completes; if the add later rolls back (e.g. Curl_update_timer fails and the transfer is removed) the code removes the transfer but does not undo the ++, causing the “total ever” to overcount failed adds. Either move the increment to after the add is guaranteed to succeed, or decrement on all rollback paths so the counter strictly reflects one successful increment per added transfer.

Comment on lines +282 to +357
static CURLcode rtsp_setup_body(struct Curl_easy *data,
Curl_RtspReq rtspreq,
struct dynbuf *reqp)
{
CURLcode result;
if(rtspreq == RTSPREQ_ANNOUNCE ||
rtspreq == RTSPREQ_SET_PARAMETER ||
rtspreq == RTSPREQ_GET_PARAMETER) {
curl_off_t req_clen; /* request content length */

if(data->state.upload) {
req_clen = data->state.infilesize;
data->state.httpreq = HTTPREQ_PUT;
result = Curl_creader_set_fread(data, req_clen);
if(result)
return result;
}
else {
if(data->set.postfields) {
size_t plen = strlen(data->set.postfields);
req_clen = (curl_off_t)plen;
result = Curl_creader_set_buf(data, data->set.postfields, plen);
}
else if(data->state.infilesize >= 0) {
req_clen = data->state.infilesize;
result = Curl_creader_set_fread(data, req_clen);
}
else {
req_clen = 0;
result = Curl_creader_set_null(data);
}
if(result)
return result;
}

if(req_clen > 0) {
/* As stated in the http comments, it is probably not wise to
* actually set a custom Content-Length in the headers */
if(!Curl_checkheaders(data, STRCONST("Content-Length"))) {
result = curlx_dyn_addf(reqp, "Content-Length: %" FMT_OFF_T"\r\n",
req_clen);
if(result)
return result;
}

if(rtspreq == RTSPREQ_SET_PARAMETER ||
rtspreq == RTSPREQ_GET_PARAMETER) {
if(!Curl_checkheaders(data, STRCONST("Content-Type"))) {
result = curlx_dyn_addn(reqp,
STRCONST("Content-Type: "
"text/parameters\r\n"));
if(result)
return result;
}
}

if(rtspreq == RTSPREQ_ANNOUNCE) {
if(!Curl_checkheaders(data, STRCONST("Content-Type"))) {
result = curlx_dyn_addn(reqp,
STRCONST("Content-Type: "
"application/sdp\r\n"));
if(result)
return result;
}
}
}
else if(rtspreq == RTSPREQ_GET_PARAMETER) {
/* Check for an empty GET_PARAMETER (heartbeat) request */
data->state.httpreq = HTTPREQ_HEAD;
data->req.no_body = TRUE;
}
}
else
result = Curl_creader_set_null(data);
return result;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Respect CURLOPT_POSTFIELDSIZE for RTSP request bodies.

rtsp_setup_body() uses strlen() on postfields and ignores postfieldsize, preventing binary payloads and truncating data with embedded NULs.

Apply this diff:

-      if(data->set.postfields) {
-        size_t plen = strlen(data->set.postfields);
-        req_clen = (curl_off_t)plen;
-        result = Curl_creader_set_buf(data, data->set.postfields, plen);
+      if(data->set.postfields) {
+        size_t plen;
+        if(data->set.postfieldsize >= 0)
+          plen = (size_t)data->set.postfieldsize;
+        else
+          plen = strlen(data->set.postfields);
+        req_clen = (curl_off_t)plen;
+        result = Curl_creader_set_buf(data, data->set.postfields, plen);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
static CURLcode rtsp_setup_body(struct Curl_easy *data,
Curl_RtspReq rtspreq,
struct dynbuf *reqp)
{
CURLcode result;
if(rtspreq == RTSPREQ_ANNOUNCE ||
rtspreq == RTSPREQ_SET_PARAMETER ||
rtspreq == RTSPREQ_GET_PARAMETER) {
curl_off_t req_clen; /* request content length */
if(data->state.upload) {
req_clen = data->state.infilesize;
data->state.httpreq = HTTPREQ_PUT;
result = Curl_creader_set_fread(data, req_clen);
if(result)
return result;
}
else {
if(data->set.postfields) {
size_t plen = strlen(data->set.postfields);
req_clen = (curl_off_t)plen;
result = Curl_creader_set_buf(data, data->set.postfields, plen);
}
else if(data->state.infilesize >= 0) {
req_clen = data->state.infilesize;
result = Curl_creader_set_fread(data, req_clen);
}
else {
req_clen = 0;
result = Curl_creader_set_null(data);
}
if(result)
return result;
}
if(req_clen > 0) {
/* As stated in the http comments, it is probably not wise to
* actually set a custom Content-Length in the headers */
if(!Curl_checkheaders(data, STRCONST("Content-Length"))) {
result = curlx_dyn_addf(reqp, "Content-Length: %" FMT_OFF_T"\r\n",
req_clen);
if(result)
return result;
}
if(rtspreq == RTSPREQ_SET_PARAMETER ||
rtspreq == RTSPREQ_GET_PARAMETER) {
if(!Curl_checkheaders(data, STRCONST("Content-Type"))) {
result = curlx_dyn_addn(reqp,
STRCONST("Content-Type: "
"text/parameters\r\n"));
if(result)
return result;
}
}
if(rtspreq == RTSPREQ_ANNOUNCE) {
if(!Curl_checkheaders(data, STRCONST("Content-Type"))) {
result = curlx_dyn_addn(reqp,
STRCONST("Content-Type: "
"application/sdp\r\n"));
if(result)
return result;
}
}
}
else if(rtspreq == RTSPREQ_GET_PARAMETER) {
/* Check for an empty GET_PARAMETER (heartbeat) request */
data->state.httpreq = HTTPREQ_HEAD;
data->req.no_body = TRUE;
}
}
else
result = Curl_creader_set_null(data);
return result;
}
static CURLcode rtsp_setup_body(struct Curl_easy *data,
Curl_RtspReq rtspreq,
struct dynbuf *reqp)
{
CURLcode result;
if(rtspreq == RTSPREQ_ANNOUNCE ||
rtspreq == RTSPREQ_SET_PARAMETER ||
rtspreq == RTSPREQ_GET_PARAMETER) {
curl_off_t req_clen; /* request content length */
if(data->state.upload) {
req_clen = data->state.infilesize;
data->state.httpreq = HTTPREQ_PUT;
result = Curl_creader_set_fread(data, req_clen);
if(result)
return result;
}
else {
if(data->set.postfields) {
size_t plen;
if(data->set.postfieldsize >= 0)
plen = (size_t)data->set.postfieldsize;
else
plen = strlen(data->set.postfields);
req_clen = (curl_off_t)plen;
result = Curl_creader_set_buf(data, data->set.postfields, plen);
}
else if(data->state.infilesize >= 0) {
req_clen = data->state.infilesize;
result = Curl_creader_set_fread(data, req_clen);
}
else {
req_clen = 0;
result = Curl_creader_set_null(data);
}
if(result)
return result;
}
if(req_clen > 0) {
/* As stated in the http comments, it is probably not wise to
* actually set a custom Content-Length in the headers */
if(!Curl_checkheaders(data, STRCONST("Content-Length"))) {
result = curlx_dyn_addf(reqp, "Content-Length: %" FMT_OFF_T"\r\n",
req_clen);
if(result)
return result;
}
if(rtspreq == RTSPREQ_SET_PARAMETER ||
rtspreq == RTSPREQ_GET_PARAMETER) {
if(!Curl_checkheaders(data, STRCONST("Content-Type"))) {
result = curlx_dyn_addn(reqp,
STRCONST("Content-Type: "
"text/parameters\r\n"));
if(result)
return result;
}
}
if(rtspreq == RTSPREQ_ANNOUNCE) {
if(!Curl_checkheaders(data, STRCONST("Content-Type"))) {
result = curlx_dyn_addn(reqp,
STRCONST("Content-Type: "
"application/sdp\r\n"));
if(result)
return result;
}
}
}
else if(rtspreq == RTSPREQ_GET_PARAMETER) {
/* Check for an empty GET_PARAMETER (heartbeat) request */
data->state.httpreq = HTTPREQ_HEAD;
data->req.no_body = TRUE;
}
}
else
result = Curl_creader_set_null(data);
return result;
}
🤖 Prompt for AI Agents
In deps/curl/lib/rtsp.c around lines 282 to 357, rtsp_setup_body() currently
uses strlen(data->set.postfields) and ignores CURLOPT_POSTFIELDSIZE; change it
to respect data->set.postfieldsize when the caller provided it (use that size as
the payload length and pass it to Curl_creader_set_buf), falling back to
strlen() only when postfieldsize is not set/negative; ensure you cast/validate
the curl_off_t to size_t safely and set req_clen from the postfieldsize value so
binary payloads and embedded NULs are handled correctly.

Comment on lines +885 to 888
if(err != -1)
/* successful rewind */
return CURLE_OK;
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Bug: incorrect fseek() success check (treats errors as success)

fseek returns 0 on success and non‑zero on failure. if (err != -1) will wrongly treat most failures as success. Fix to if (err == 0).

-      if(err != -1)
-        /* successful rewind */
-        return CURLE_OK;
+      if(err == 0)
+        /* successful rewind */
+        return CURLE_OK;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if(err != -1)
/* successful rewind */
return CURLE_OK;
}
if(err == 0)
/* successful rewind */
return CURLE_OK;
}
🤖 Prompt for AI Agents
In deps/curl/lib/sendf.c around lines 885 to 888, the fseek() success check is
inverted: it currently treats any err value not equal to -1 as success. Change
the conditional to check for zero (err == 0) since fseek returns 0 on success
and non‑zero on failure, so that only successful rewinds return CURLE_OK; leave
failure handling untouched.

@santigimeno santigimeno changed the base branch from node-v22.x-nsolid-v5.x to node-v22.x-nsolid-v6.x September 23, 2025 08:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants