CVE-2026-3805: use after free in SMB connection reuse
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
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.
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
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.
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.
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.
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.
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)
Source code
| 373 | |
| 374 | static 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 | |
| 385 | static 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 | |
| 397 | static 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 */ |
| 442 | static 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 */ |
| 745 | static 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 | } |
| 515 | void 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 | } |
Show 31 lines (not cited by the judge)
| 297 | static 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 | */ |
| 304 | const 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 | }; |
Show 46 lines (not cited by the judge)
| 514 | void 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 | } |
Show 161 lines (not cited by the judge)
| 998 | static 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; |
Show 161 lines (not cited by the judge)
| 3425 | static 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"); |