✓ProveIt
Examples / Report #3591944 Saved analysis

CVE-2026-3805: use after free in SMB connection reuse

curl · 2026-03-0814.6s analysis · $0.024 in model calls

What the report claims

A heap-use-after-free occurs in smb_send_open() when processing two SMB URLs targeting the same host, causing a crash or potential memory corruption.

Compare with curl's recorded outcome

Known vulnerability

A real vulnerability: CVE-2026-3805 use after free in SMB connection reuse

Shown for comparison after analysis. The pipeline did not see this label.

ProveIt's finding

Code supports the claim

The code supports the reported flaw. A maintainer should review its impact and decide what to do next.

Why this finding?
  • The code contains a use-after-free: req->path points into connection-owned smbc->share, which is freed during connection reuse while req remains on the easy handle.
  • The smb_easy_dtor comment explicitly acknowledges this exact issue: 'If the connection is destroyed first, req->path points to free'd memory.'
  • The flaw is triggered by a normal, legitimate command line (two SMB URLs to the same host), not a crafted exploit command.
  • The UAF causes a guaranteed crash (DoS) and is a heap-use-after-free (CWE-416), which could potentially be exploited further if the freed heap is reallocated with attacker-influenced data.
  • The project's 'not security issues' list does not cover client-side memory-safety bugs triggered by normal usage; the 'Tricking a user' section applies to misleading command lines, not legitimate multi-URL transfers, and the 'NULL dereferences and crashes' section addresses server-triggered crashes.
How this analysis ran 5 claims · 10 citations verified

Checked against curl master on 2026-03-08 (9a674ee6f7ba). Dev evaluation split. Citation verification checks that a referenced line was in the evidence; it does not prove the model interpreted it correctly.

1 · Grounding
grounded
no model · 11 references checked out, 0 did not
2 · Claims
NVIDIA-Nemotron-3-Nano-30B-A3B
5 claims · 1810 tokens · $0.0002
3 · Evidence
7 code snippets
real curl source at 9a674ee6f7ba
4 · Judgment
Nemotron-3-Ultra-550b-a55b
16588 tokens · 12.4s · $0.0238
5 · Receipts
10 of 10 citations verified
every citation must point at code the judge was shown
Review a draft reply to the reporter

Edit and verify before sending.

Thank you for the detailed report. The code confirms the use-after-free: req->path points into smbc->share (lib/smb.c:431), which is freed in smb_conn_dtor (line 390) when the connection is reused, and smb_send_open reads the dangling pointer via strlen (line 750). The comment in smb_easy_dtor (lines 379-381) acknowledges this exact issue. This is a valid security bug reachable by a normal two-URL command line. We will prioritize a fix.

Inspect all claims and source code 5 claims · 7 code excerpts

Report claims

C1flaw supported

A use-after-free occurs in smb_send_open() when processing two SMB URLs targeting the same host

Read the reasoning

The code shows req->path is set to a pointer inside smbc->share (line 431). When connection reuse occurs, the old connection's smb_conn_dtor frees smbc->share (line 390), but req on the easy handle still holds the dangling pointer. smb_send_open then reads it via strlen (line 750). The code's own comment confirms this exact issue.

C2location supported

The flaw is located in lib/smb.c at smb_send_open() and smb_parse_url_path()

Read the reasoning

Both functions exist in lib/smb.c at the cited locations and contain the relevant code.

C3trigger supported

Triggered by sending two SMB URLs to the same host, causing connection reuse and freeing of the needle connection

Read the reasoning

Connection reuse frees the needle connection via Curl_conn_free, which destroys the meta_hash and runs smb_conn_dtor, freeing smbc->share while req->path still points into it.

C4impact supported

Results in a guaranteed crash (denial of service) and potential memory corruption if the freed heap is reallocated with attacker data

Read the reasoning

The UAF read in smb_send_open causes a guaranteed crash (DoS). The freed 13-byte region could be reallocated with attacker-influenced data, potentially leading to information disclosure or memory corruption, though the report does not demonstrate exploitation beyond DoS.

C5reproduction supported

Reproduced by running curl with two SMB URLs to the same host after building with ASAN and SMB enabled

Read the reasoning

The reproduction steps are concrete and the code confirms the bug is reachable by the described two-URL command line. The fake SMB server is only needed to accept the connection; the UAF occurs client-side before any server response.

Read the original report (excerpt)

Full report on HackerOne ↗

## Summary A heap-use-after-free occurs in `smb_send_open()` at `lib/smb.c` when curl processes two SMB URLs targeting the same host. The function `smb_parse_url_path()` sets `req->path` as a non-owning pointer into `smbc->share` (connection-owned memory). During connection reuse, the needle connection is freed via `Curl_conn_free()` → `smb_conn_dtor()`, which frees `smbc->share`, but `req->path` (on the easy handle) still references the freed buffer. The subsequent `strlen(req->path)` in `smb_send_open()` reads freed heap memory. ## Affected Version curl 8.19.0-DEV (master branch, built March 8 2026) Platform: Ubuntu 22.04 on x86_64 (WSL2) Built with: gcc, OpenSSL, --enable-smb, -fsanitize=address ## Steps To Reproduce 1. Clone and build curl from master with ASAN and SMB enabled: git clone https://github.com/curl/curl.git && cd curl autoreconf -fi ./configure --with-openssl --enable-smb --without-libpsl make CFLAGS="-fsanitize=address -g -O0 -fno-omit-frame-pointer" LDFLAGS="-fsanitize=address" -j$(nproc) 2. Start the attached fake SMB server (fake_smb_server.py) in one terminal: python3 fake_smb_server.py 5445 3. In another terminal, run curl with two SMB URLs to the same host: ASAN_OPTIONS=detect_leaks=0 LD_LIBRARY_PATH=./lib/.libs \ ./src/.libs/curl -u guest:guest \ "smb://127.0.0.1:5445/share1/file1" -o /dev/null \ "smb://127.0.0.1:5445/share2/file2" -o /dev/null 4. ASAN reports heap-use-after-free in smb_send_open. ## Root Cause In `smb_parse_url_path()` (lib/smb.c ~line 398-435): - `smbc->share = strdup(path)` allocates "share2/file2" (13 bytes) - The `/` is replaced with `\0`, splitting it into "share2\0file2\0" - `req->path = slash` points 7 bytes into `smbc->share` When the second URL reuses the pooled connection from URL #1: - `url_find_or_create_conn()` finds a match and frees the needle connection - `Curl_conn_free()` → `smb_conn_dtor()` → `free(smbc->share)` - `req->path` now dangles into freed heap Then `smb_request_state()` → `smb_send_open()`: ```c const size_t byte_count = strlen(req->path) + 1; // UAF read ## Impact Heap-use-after-free (CWE-416) triggered by a simple two-URL curl command line. Requires no authentication to a real SMB server the UAF occurs client-side before any server response to the second request. This results in a guaranteed crash (DoS). If the freed 13-byte he …

Source code

lib/smb.clines 373–460 · lines 398-435 cited by the report
373
374static void smb_easy_dtor(void *key, size_t klen, void *entry)
375{
376 struct smb_request *req = entry;
377 (void)key;
378 (void)klen;
379 /* `req->path` points to somewhere in `struct smb_conn` which is
C1: Comment in smb_easy_dtor explicitly acknowledges: 'If the connection is destroyed first, req->path points to free'd memory.'
C5: Code comment confirms the bug exists and is reachable via connection reuse
380 * kept at the connection meta. If the connection is destroyed first,
381 * req->path points to free'd memory. */
382 curlx_free(req);
383}
384
385static void smb_conn_dtor(void *key, size_t klen, void *entry)
386{
387 struct smb_conn *smbc = entry;
388 (void)key;
389 (void)klen;
390 Curl_safefree(smbc->share);
C1: smb_conn_dtor frees smbc->share via Curl_safefree
C3: smb_conn_dtor frees smbc->share
391 Curl_safefree(smbc->domain);
392 Curl_safefree(smbc->recv_buf);
393 Curl_safefree(smbc->send_buf);
394 curlx_free(smbc);
395}
396
397static CURLcode smb_parse_url_path(struct Curl_easy *data,
C2: smb_parse_url_path definition starts here
398 struct smb_conn *smbc,
399 struct smb_request *req)
400{
401 char *path;
402 char *slash;
403 CURLcode result;
404
405 /* URL decode the path */
406 result = Curl_urldecode(data->state.up.path, 0, &path, NULL, REJECT_CTRL);
407 if(result)
408 return result;
409
410 /* Parse the path for the share */
411 smbc->share = curlx_strdup((*path == '/' || *path == '\\')
412 ? path + 1 : path);
413 curlx_free(path);
414 if(!smbc->share)
415 return CURLE_OUT_OF_MEMORY;
416
417 slash = strchr(smbc->share, '/');
418 if(!slash)
419 slash = strchr(smbc->share, '\\');
420
421 /* The share must be present */
422 if(!slash) {
423 Curl_safefree(smbc->share);
424 failf(data, "missing share in URL path for SMB");
425 return CURLE_URL_MALFORMAT;
426 }
427
428 /* Parse the path for the file path converting any forward slashes into
429 backslashes */
430 *slash++ = 0;
431 req->path = slash;
C1: req->path = slash; makes req->path point into smbc->share
432
433 for(; *slash; slash++) {
434 if(*slash == '/')
435 *slash = '\\';
436 }
437 return CURLE_OK;
438}
439
440/* this should setup things in the connection, not in the easy
441 handle */
442static CURLcode smb_setup_connection(struct Curl_easy *data,
443 struct connectdata *conn)
444{
445 struct smb_conn *smbc;
446 struct smb_request *req;
447
448 /* Initialize the connection state */
449 smbc = curlx_calloc(1, sizeof(*smbc));
450 if(!smbc ||
451 Curl_conn_meta_set(conn, CURL_META_SMB_CONN, smbc, smb_conn_dtor))
452 return CURLE_OUT_OF_MEMORY;
453
454 /* Initialize the request state */
455 req = curlx_calloc(1, sizeof(*req));
456 if(!req ||
457 Curl_meta_set(data, CURL_META_SMB_EASY, req, smb_easy_dtor))
458 return CURLE_OUT_OF_MEMORY;
459
460 /* Parse the URL path */
lib/smb.clines 745–773 · definition of smb_send_open
745static CURLcode smb_send_open(struct Curl_easy *data,
C2: smb_send_open definition starts here
746 struct smb_conn *smbc,
747 struct smb_request *req)
748{
749 struct smb_nt_create msg;
750 const size_t byte_count = strlen(req->path) + 1;
C1: smb_send_open calls strlen(req->path) reading the dangling pointer
C4: strlen(req->path) reads freed memory, causing UAF
751
752 if(byte_count > sizeof(msg.bytes))
753 return CURLE_FILESIZE_EXCEEDED;
754
755 memset(&msg, 0, sizeof(msg) - sizeof(msg.bytes));
756 msg.word_count = SMB_WC_NT_CREATE_ANDX;
757 msg.andx.command = SMB_COM_NO_ANDX_COMMAND;
758 msg.name_length = smb_swap16((unsigned short)(byte_count - 1));
759 msg.share_access = smb_swap32(SMB_FILE_SHARE_ALL);
760 if(data->state.upload) {
761 msg.access = smb_swap32(SMB_GENERIC_READ | SMB_GENERIC_WRITE);
762 msg.create_disposition = smb_swap32(SMB_FILE_OVERWRITE_IF);
763 }
764 else {
765 msg.access = smb_swap32(SMB_GENERIC_READ);
766 msg.create_disposition = smb_swap32(SMB_FILE_OPEN);
767 }
768 msg.byte_count = smb_swap16((unsigned short)byte_count);
769 curlx_strcopy(msg.bytes, sizeof(msg.bytes), req->path, byte_count - 1);
770
771 return smb_send_message(data, smbc, req, SMB_COM_NT_CREATE_ANDX, &msg,
772 sizeof(msg) - sizeof(msg.bytes) + byte_count);
773}
lib/url.clines 515–559 · definition of Curl_conn_free
515void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn)
516{
517 size_t i;
518
519 DEBUGASSERT(conn);
520
521 if(conn->scheme && conn->scheme->run->disconnect &&
522 !conn->bits.shutdown_handler)
523 conn->scheme->run->disconnect(data, conn, TRUE);
524
525 for(i = 0; i < CURL_ARRAYSIZE(conn->cfilter); ++i) {
526 Curl_conn_cf_discard_all(data, conn, (int)i);
527 }
528
529 Curl_free_idnconverted_hostname(&conn->host);
530 Curl_free_idnconverted_hostname(&conn->conn_to_host);
531#ifndef CURL_DISABLE_PROXY
532 Curl_free_idnconverted_hostname(&conn->http_proxy.host);
533 Curl_free_idnconverted_hostname(&conn->socks_proxy.host);
534 Curl_safefree(conn->http_proxy.user);
535 Curl_safefree(conn->socks_proxy.user);
536 Curl_safefree(conn->http_proxy.passwd);
537 Curl_safefree(conn->socks_proxy.passwd);
538 Curl_safefree(conn->http_proxy.host.rawalloc); /* http proxy name buffer */
539 Curl_safefree(conn->socks_proxy.host.rawalloc); /* socks proxy name buffer */
540#endif
541 Curl_safefree(conn->user);
542 Curl_safefree(conn->passwd);
543 Curl_safefree(conn->sasl_authzid);
544 Curl_safefree(conn->options);
545 Curl_safefree(conn->oauth_bearer);
546 Curl_safefree(conn->host.rawalloc); /* hostname buffer */
547 Curl_safefree(conn->conn_to_host.rawalloc); /* hostname buffer */
548 Curl_safefree(conn->secondaryhostname);
549 Curl_safefree(conn->localdev);
550 Curl_ssl_conn_config_cleanup(conn);
551
552#ifdef USE_UNIX_SOCKETS
553 Curl_safefree(conn->unix_domain_socket);
554#endif
555 Curl_safefree(conn->destination);
556 Curl_hash_destroy(&conn->meta_hash);
C3: Curl_conn_free destroys conn->meta_hash, which invokes smb_conn_dtor
557
558 curlx_free(conn); /* free all the connection oriented data */
559}
lib/smb.c@curl-8_18_0lines 297–327 · definition of smb_parse_url_path as in release curl-8_18_0; differs from master; commits that changed it since: 07926b5982 lib: reorder protocol functions to avoid forward declarations (misc)
Show 31 lines (not cited by the judge)
297static CURLcode smb_parse_url_path(struct Curl_easy *data,
298 struct smb_conn *smbc,
299 struct smb_request *req);
300
301/*
302 * SMB handler interface
303 */
304const struct Curl_handler Curl_handler_smb = {
305 "smb", /* scheme */
306 smb_setup_connection, /* setup_connection */
307 smb_do, /* do_it */
308 ZERO_NULL, /* done */
309 ZERO_NULL, /* do_more */
310 smb_connect, /* connect_it */
311 smb_connection_state, /* connecting */
312 smb_request_state, /* doing */
313 smb_pollset, /* proto_pollset */
314 smb_pollset, /* doing_pollset */
315 ZERO_NULL, /* domore_pollset */
316 ZERO_NULL, /* perform_pollset */
317 ZERO_NULL, /* disconnect */
318 ZERO_NULL, /* write_resp */
319 ZERO_NULL, /* write_resp_hd */
320 ZERO_NULL, /* connection_check */
321 ZERO_NULL, /* attach connection */
322 ZERO_NULL, /* follow */
323 PORT_SMB, /* defport */
324 CURLPROTO_SMB, /* protocol */
325 CURLPROTO_SMB, /* family */
326 PROTOPT_CONN_REUSE /* flags */
327};
lib/url.c@curl-8_18_0lines 514–559 · definition of Curl_conn_free as in release curl-8_18_0; differs from master; commits that changed it since: bfb79de206 urldata: byebye `conn->hostname_resolve` | 8edc0338f3 lib: separate scheme info from protocol implementation
Show 46 lines (not cited by the judge)
514void Curl_conn_free(struct Curl_easy *data, struct connectdata *conn)
515{
516 size_t i;
517
518 DEBUGASSERT(conn);
519
520 if(conn->handler && conn->handler->disconnect &&
521 !conn->bits.shutdown_handler)
522 conn->handler->disconnect(data, conn, TRUE);
523
524 for(i = 0; i < CURL_ARRAYSIZE(conn->cfilter); ++i) {
525 Curl_conn_cf_discard_all(data, conn, (int)i);
526 }
527
528 Curl_free_idnconverted_hostname(&conn->host);
529 Curl_free_idnconverted_hostname(&conn->conn_to_host);
530#ifndef CURL_DISABLE_PROXY
531 Curl_free_idnconverted_hostname(&conn->http_proxy.host);
532 Curl_free_idnconverted_hostname(&conn->socks_proxy.host);
533 Curl_safefree(conn->http_proxy.user);
534 Curl_safefree(conn->socks_proxy.user);
535 Curl_safefree(conn->http_proxy.passwd);
536 Curl_safefree(conn->socks_proxy.passwd);
537 Curl_safefree(conn->http_proxy.host.rawalloc); /* http proxy name buffer */
538 Curl_safefree(conn->socks_proxy.host.rawalloc); /* socks proxy name buffer */
539#endif
540 Curl_safefree(conn->user);
541 Curl_safefree(conn->passwd);
542 Curl_safefree(conn->sasl_authzid);
543 Curl_safefree(conn->options);
544 Curl_safefree(conn->oauth_bearer);
545 Curl_safefree(conn->host.rawalloc); /* hostname buffer */
546 Curl_safefree(conn->conn_to_host.rawalloc); /* hostname buffer */
547 Curl_safefree(conn->hostname_resolve);
548 Curl_safefree(conn->secondaryhostname);
549 Curl_safefree(conn->localdev);
550 Curl_ssl_conn_config_cleanup(conn);
551
552#ifdef USE_UNIX_SOCKETS
553 Curl_safefree(conn->unix_domain_socket);
554#endif
555 Curl_safefree(conn->destination);
556 Curl_hash_destroy(&conn->meta_hash);
557
558 curlx_free(conn); /* free all the connection oriented data */
559}
lib/smb.clines 998–1158 · definition of smb_request_state (truncated)
Show 161 lines (not cited by the judge)
998static CURLcode smb_request_state(struct Curl_easy *data, bool *done)
999{
1000 struct connectdata *conn = data->conn;
1001 struct smb_conn *smbc = Curl_conn_meta_get(conn, CURL_META_SMB_CONN);
1002 struct smb_request *req = Curl_meta_get(data, CURL_META_SMB_EASY);
1003 struct smb_header *h;
1004 enum smb_req_state next_state = SMB_DONE;
1005 unsigned short len;
1006 unsigned short off;
1007 CURLcode result;
1008 void *msg = NULL;
1009 const struct smb_nt_create_response *smb_m;
1010
1011 if(!smbc || !req)
1012 return CURLE_FAILED_INIT;
1013
1014 if(data->state.upload && (data->state.infilesize < 0)) {
1015 failf(data, "SMB upload needs to know the size up front");
1016 return CURLE_SEND_ERROR;
1017 }
1018
1019 /* Start the request */
1020 if(req->state == SMB_REQUESTING) {
1021 result = smb_send_tree_connect(data, smbc, req);
1022 if(result) {
1023 connclose(conn, "SMB: failed to send tree connect message");
1024 return result;
1025 }
1026
1027 request_state(data, SMB_TREE_CONNECT);
1028 }
1029
1030 /* Send the previous message and check for a response */
1031 result = smb_send_and_recv(data, smbc, &msg);
1032 if(result && result != CURLE_AGAIN) {
1033 connclose(conn, "SMB: failed to communicate");
1034 return result;
1035 }
1036
1037 if(!msg)
1038 return CURLE_OK;
1039
1040 h = msg;
1041
1042 switch(req->state) {
1043 case SMB_TREE_CONNECT:
1044 if(h->status) {
1045 req->result = CURLE_REMOTE_FILE_NOT_FOUND;
1046 if(h->status == smb_swap32(SMB_ERR_NOACCESS))
1047 req->result = CURLE_REMOTE_ACCESS_DENIED;
1048 break;
1049 }
1050 req->tid = smb_swap16(h->tid);
1051 next_state = SMB_OPEN;
1052 break;
1053
1054 case SMB_OPEN:
1055 if(h->status || smbc->got < sizeof(struct smb_nt_create_response)) {
1056 req->result = CURLE_REMOTE_FILE_NOT_FOUND;
1057 if(h->status == smb_swap32(SMB_ERR_NOACCESS))
1058 req->result = CURLE_REMOTE_ACCESS_DENIED;
1059 next_state = SMB_TREE_DISCONNECT;
1060 break;
1061 }
1062 smb_m = (const struct smb_nt_create_response *)msg;
1063 req->fid = smb_swap16(smb_m->fid);
1064 data->req.offset = 0;
1065 if(data->state.upload) {
1066 data->req.size = data->state.infilesize;
1067 Curl_pgrsSetUploadSize(data, data->req.size);
1068 next_state = SMB_UPLOAD;
1069 }
1070 else {
1071 data->req.size = smb_swap64(smb_m->end_of_file);
1072 if(data->req.size < 0) {
1073 req->result = CURLE_WEIRD_SERVER_REPLY;
1074 next_state = SMB_CLOSE;
1075 }
1076 else {
1077 Curl_pgrsSetDownloadSize(data, data->req.size);
1078 if(data->set.get_filetime)
1079 get_posix_time(&data->info.filetime, smb_m->last_change_time);
1080 next_state = SMB_DOWNLOAD;
1081 }
1082 }
1083 break;
1084
1085 case SMB_DOWNLOAD:
1086 if(h->status || smbc->got < sizeof(struct smb_header) + 15) {
1087 req->result = CURLE_RECV_ERROR;
1088 next_state = SMB_CLOSE;
1089 break;
1090 }
1091 len = Curl_read16_le((const unsigned char *)msg +
1092 sizeof(struct smb_header) + 11);
1093 off = Curl_read16_le((const unsigned char *)msg +
1094 sizeof(struct smb_header) + 13);
1095 if(len > 0) {
1096 if(off + sizeof(unsigned int) + len > smbc->got) {
1097 failf(data, "Invalid input packet");
1098 result = CURLE_RECV_ERROR;
1099 }
1100 else
1101 result = Curl_client_write(data, CLIENTWRITE_BODY,
1102 (char *)msg + off + sizeof(unsigned int),
1103 len);
1104 if(result) {
1105 req->result = result;
1106 next_state = SMB_CLOSE;
1107 break;
1108 }
1109 }
1110 data->req.offset += len;
1111 next_state = (len < MAX_PAYLOAD_SIZE) ? SMB_CLOSE : SMB_DOWNLOAD;
1112 break;
1113
1114 case SMB_UPLOAD:
1115 if(h->status || smbc->got < sizeof(struct smb_header) + 7) {
1116 req->result = CURLE_UPLOAD_FAILED;
1117 next_state = SMB_CLOSE;
1118 break;
1119 }
1120 len = Curl_read16_le((const unsigned char *)msg +
1121 sizeof(struct smb_header) + 5);
1122 data->req.bytecount += len;
1123 data->req.offset += len;
1124 Curl_pgrs_upload_inc(data, len);
1125 if(data->req.bytecount >= data->req.size)
1126 next_state = SMB_CLOSE;
1127 else
1128 next_state = SMB_UPLOAD;
1129 break;
1130
1131 case SMB_CLOSE:
1132 /* We do not care if the close failed, proceed to tree disconnect anyway */
1133 next_state = SMB_TREE_DISCONNECT;
1134 break;
1135
1136 case SMB_TREE_DISCONNECT:
1137 next_state = SMB_DONE;
1138 break;
1139
1140 default:
1141 smb_pop_message(smbc);
1142 return CURLE_OK; /* ignore */
1143 }
1144
1145 smb_pop_message(smbc);
1146
1147 switch(next_state) {
1148 case SMB_OPEN:
1149 result = smb_send_open(data, smbc, req);
1150 break;
1151
1152 case SMB_DOWNLOAD:
1153 result = smb_send_read(data, smbc, req);
1154 break;
1155
1156 case SMB_UPLOAD:
1157 result = smb_send_write(data, smbc, req);
1158 break;
lib/url.clines 3425–3585 · definition of url_find_or_create_conn (truncated)
Show 161 lines (not cited by the judge)
3425static CURLcode url_find_or_create_conn(struct Curl_easy *data)
3426{
3427 struct connectdata *needle = NULL;
3428 bool waitpipe = FALSE;
3429 CURLcode result;
3430
3431 /* create the template connection for transfer data. Use this needle to
3432 * find an existing connection or, if none exists, convert needle
3433 * to a full connection and attach it to data. */
3434 result = url_create_needle(data, &needle);
3435 if(result)
3436 goto out;
3437 DEBUGASSERT(needle);
3438
3439 /***********************************************************************
3440 * file: is a special case in that it does not need a network connection
3441 ***********************************************************************/
3442#ifndef CURL_DISABLE_FILE
3443 if(needle->scheme->flags & PROTOPT_NONETWORK) {
3444 bool done;
3445 /* this is supposed to be the connect function so we better at least check
3446 that the file is present here! */
3447 DEBUGASSERT(needle->scheme->run->connect_it);
3448 data->info.conn_scheme = needle->scheme->name;
3449 /* conn_protocol can only provide "old" protocols */
3450 data->info.conn_protocol = (needle->scheme->protocol) & CURLPROTO_MASK;
3451 result = needle->scheme->run->connect_it(data, &done);
3452 if(result)
3453 goto out;
3454
3455 /* Setup a "faked" transfer that will do nothing */
3456 Curl_attach_connection(data, needle);
3457 needle = NULL;
3458 result = Curl_cpool_add(data, data->conn);
3459 if(!result) {
3460 /* Setup whatever necessary for a resumed transfer */
3461 result = setup_range(data);
3462 if(!result) {
3463 Curl_xfer_setup_nop(data);
3464 result = Curl_init_do(data, data->conn);
3465 }
3466 }
3467
3468 if(result) {
3469 DEBUGASSERT(data->conn->scheme->run->done);
3470 /* we ignore the return code for the protocol-specific DONE */
3471 (void)data->conn->scheme->run->done(data, result, FALSE);
3472 }
3473 goto out;
3474 }
3475#endif
3476
3477 /* Complete the easy's SSL configuration for connection cache matching */
3478 result = Curl_ssl_easy_config_complete(data);
3479 if(result)
3480 goto out;
3481
3482 /* Get rid of any dead connections so limit are easier kept. */
3483 Curl_cpool_prune_dead(data);
3484
3485 /*************************************************************
3486 * Reuse of existing connection is not allowed when
3487 * - connect_only is set or
3488 * - reuse_fresh is set and this is not a follow-up request
3489 * (like with HTTP followlocation)
3490 *************************************************************/
3491 if((!data->set.reuse_fresh || data->state.followlocation) &&
3492 !data->set.connect_only) {
3493 /* Ok, try to find and attach an existing one */
3494 url_attach_existing(data, needle, &waitpipe);
3495 }
3496
3497 if(data->conn) {
3498 /* We attached an existing connection for this transfer. Copy
3499 * over transfer specific properties over from needle. */
3500 struct connectdata *conn = data->conn;
3501 VERBOSE(bool tls_upgraded = (!(needle->given->flags & PROTOPT_SSL) &&
3502 Curl_conn_is_ssl(conn, FIRSTSOCKET)));
3503
3504 conn->bits.reuse = TRUE;
3505 url_conn_reuse_adjust(data, needle);
3506
3507#ifndef CURL_DISABLE_PROXY
3508 infof(data, "Reusing existing %s: connection%s with %s %s",
3509 conn->given->name,
3510 tls_upgraded ? " (upgraded to SSL)" : "",
3511 conn->bits.proxy ? "proxy" : "host",
3512 conn->socks_proxy.host.name ? conn->socks_proxy.host.dispname :
3513 conn->http_proxy.host.name ? conn->http_proxy.host.dispname :
3514 conn->host.dispname);
3515#else
3516 infof(data, "Reusing existing %s: connection%s with host %s",
3517 conn->given->name,
3518 tls_upgraded ? " (upgraded to SSL)" : "",
3519 conn->host.dispname);
3520#endif
3521 }
3522 else {
3523 /* We have decided that we want a new connection. We may not be able to do
3524 that if we have reached the limit of how many connections we are
3525 allowed to open. */
3526 DEBUGF(infof(data, "new connection, bits.close=%d", needle->bits.close));
3527
3528 if(waitpipe) {
3529 /* There is a connection that *might* become usable for multiplexing
3530 "soon", and we wait for that */
3531 infof(data, "Waiting on connection to negotiate possible multiplexing.");
3532 result = CURLE_NO_CONNECTION_AVAILABLE;
3533 goto out;
3534 }
3535 else {
3536 switch(Curl_cpool_check_limits(data, needle)) {
3537 case CPOOL_LIMIT_DEST:
3538 infof(data, "No more connections allowed to host");
3539 result = CURLE_NO_CONNECTION_AVAILABLE;
3540 goto out;
3541 case CPOOL_LIMIT_TOTAL:
3542 if(data->master_mid != UINT32_MAX)
3543 CURL_TRC_M(data, "Allowing sub-requests (like DoH) to override "
3544 "max connection limit");
3545 else {
3546 infof(data, "No connections available, total of %zu reached.",
3547 data->multi->max_total_connections);
3548 result = CURLE_NO_CONNECTION_AVAILABLE;
3549 goto out;
3550 }
3551 break;
3552 default:
3553 break;
3554 }
3555 }
3556
3557 /* Convert needle into a full connection by filling in all the
3558 * remaining parts like the cloned SSL configuration. */
3559 result = Curl_ssl_conn_config_init(data, needle);
3560 if(result) {
3561 DEBUGF(curl_mfprintf(stderr, "Error: init connection ssl config\n"));
3562 goto out;
3563 }
3564 /* attach it and no longer own it */
3565 Curl_attach_connection(data, needle);
3566 needle = NULL;
3567
3568 result = Curl_cpool_add(data, data->conn);
3569 if(result)
3570 goto out;
3571
3572#ifdef USE_NTLM
3573 /* If NTLM is requested in a part of this connection, make sure we do not
3574 assume the state is fine as this is a fresh connection and NTLM is
3575 connection based. */
3576 if((data->state.authhost.picked & CURLAUTH_NTLM) &&
3577 data->state.authhost.done) {
3578 infof(data, "NTLM picked AND auth done set, clear picked");
3579 data->state.authhost.picked = CURLAUTH_NONE;
3580 data->state.authhost.done = FALSE;
3581 }
3582
3583 if((data->state.authproxy.picked & CURLAUTH_NTLM) &&
3584 data->state.authproxy.done) {
3585 infof(data, "NTLM-proxy picked AND auth done set, clear picked");