CVE-2022-42915: HTTP proxy double-free
What the report claims
curl frees memory twice in some cleanup function related to HTTP proxies.
Compare with curl's recorded outcome
Known vulnerability
A real vulnerability: CVE-2022-42915 HTTP proxy double free
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 at lib/http_proxy.c:174 allocates http_connect_state and stores it in conn->connect_state
- At lib/http_proxy.c:191, data->req.p.http is set to point to a struct member (&s->http_proxy) inside that allocation
- Curl_free_request_state at lib/url.c:2259 calls Curl_safefree(data->req.p.http), freeing the struct member pointer (invalid free)
- conn_free at lib/url.c:810 calls Curl_safefree(conn->connect_state), freeing the original allocation pointer (double-free)
- The valgrind output confirms both frees occur on the same 984-byte block
- The trigger (non-HTTP protocol over HTTP proxy) is a valid user-controlled input
How this analysis ran 5 claims · 10 citations verified
Checked against curl master on 2022-10-04 (a2d0154e93ee). 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 this detailed report. The double-free is confirmed: connect_init (http_proxy.c:174) allocates http_connect_state, then line 191 makes data->req.p.http point into that struct. Curl_free_request_state (url.c:2259) incorrectly frees that interior pointer, and conn_free (url.c:810) later frees the original allocation. This is reachable via `curl -x http://proxy dict://target` as you demonstrated. We'll fix this promptly.
Inspect all claims and source code 5 claims · 26 code excerpts
Report claims
curl frees memory twice in some cleanup function related to HTTP proxies
Read the reasoning
The code shows two distinct free paths for the same memory block allocated in connect_init: first via Curl_free_request_state freeing data->req.p.http (which points to a struct member), then via conn_free freeing conn->connect_state (the original allocation pointer).
the double free occurs in cleanup functions of the HTTP proxy handling code
Read the reasoning
Both free sites are in cleanup functions (Curl_free_request_state and conn_free) and the allocation occurs in HTTP proxy handling code (connect_init).
using a proxy URL with dict:// scheme, e.g. `curl -x http://localhost:80 dict://127.0.0.1`
Read the reasoning
Using a non-HTTP protocol (dict://) over an HTTP proxy triggers the proxy CONNECT path in connect_init, which performs the problematic pointer swap and allocation.
double-free leads to memory corruption
Read the reasoning
The valgrind output provided in the report shows an invalid free (freeing a non-malloc pointer) followed by a second free of the same memory block, which is a classic double-free leading to memory corruption.
run curl with valgrind on the command `curl -x http://localhost:80 dict://127.0.0.1` to see the invalid free
Read the reasoning
The valgrind output in the report directly demonstrates the reproduction steps with the exact command provided.
Deterministic checks that failed
- line none of free, realloc, curl_dbg_free, Curl_free_request_state appear near line 276 at a2d0154e93ee; it reads: 'result = operate(&global, argc, argv);'
- line none of free, realloc, curl_dbg_free, Curl_free_request_state appear near line 810 at a2d0154e93ee; it reads: 'Curl_safefree(conn->connect_state);'
- line none of free, realloc, curl_dbg_free, Curl_free_request_state appear near line 175 at a2d0154e93ee; it reads: 'mem = (Curl_ccalloc)(1, size);'
- line none of free, realloc, curl_dbg_free, Curl_free_request_state appear near line 174 at a2d0154e93ee; it reads: 's = calloc(1, sizeof(struct http_connect_state));'
Read the original report (excerpt)
Source code
| 272 | mem->size = wantedsize; |
| 273 | return mem->mem; |
| 274 | } |
| 275 | |
| 276 | return NULL; |
| 277 | } |
| 278 | |
| 279 | void curl_dbg_free(void *ptr, int line, const char *source) |
| 280 | { |
| 281 | if(ptr) { |
| 282 | struct memdebug *mem; |
| 283 | |
| 284 | #ifdef __INTEL_COMPILER |
| 285 | # pragma warning(push) |
| 286 | # pragma warning(disable:1684) |
| 287 | /* 1684: conversion from pointer to same-sized integral type */ |
| 288 | #endif |
| 289 | |
| 290 | mem = (void *)((char *)ptr - offsetof(struct memdebug, mem)); |
| 291 | |
| 292 | #ifdef __INTEL_COMPILER |
| 293 | # pragma warning(pop) |
| 294 | #endif |
| 295 | |
| 296 | /* free for real */ |
| 297 | (Curl_cfree)(mem); |
| C4: curl_dbg_free calls the real free function on the pointer | |
| 298 | } |
| 299 | |
| 300 | if(source && ptr) |
| 301 | curl_dbg_log("MEM %s:%d free(%p)\n", source, line, (void *)ptr); |
| 302 | } |
| 303 | |
| 304 | curl_socket_t curl_dbg_socket(int domain, int type, int protocol, |
| 305 | int line, const char *source) |
| 306 | { |
| 307 | const char *fmt = (sizeof(curl_socket_t) == sizeof(int)) ? |
| 308 | "FD %s:%d socket() = %d\n" : |
| 309 | (sizeof(curl_socket_t) == sizeof(long)) ? |
| 310 | "FD %s:%d socket() = %ld\n" : |
| 311 | "FD %s:%d socket() = %zd\n"; |
| 312 | |
| 313 | curl_socket_t sockfd; |
| 314 | |
| 315 | if(countcheck("socket", line, source)) |
| 316 | return CURL_SOCKET_BAD; |
| 317 | |
| 318 | sockfd = socket(domain, type, protocol); |
| 319 | |
| 320 | if(source && (sockfd != CURL_SOCKET_BAD)) |
| 321 | curl_dbg_log(fmt, source, line, sockfd); |
| 322 |
| 2234 | |
| 2235 | if(p->setup_connection) { |
| 2236 | result = (*p->setup_connection)(data, conn); |
| 2237 | |
| 2238 | if(result) |
| 2239 | return result; |
| 2240 | |
| 2241 | p = conn->handler; /* May have changed. */ |
| 2242 | } |
| 2243 | |
| 2244 | if(conn->port < 0) |
| 2245 | /* we check for -1 here since if proxy was detected already, this |
| 2246 | was very likely already set to the proxy port */ |
| 2247 | conn->port = p->defport; |
| 2248 | |
| 2249 | return CURLE_OK; |
| 2250 | } |
| 2251 | |
| 2252 | /* |
| 2253 | * Curl_free_request_state() should free temp data that was allocated in the |
| 2254 | * Curl_easy for this single request. |
| 2255 | */ |
| 2256 | |
| 2257 | void Curl_free_request_state(struct Curl_easy *data) |
| 2258 | { |
| 2259 | Curl_safefree(data->req.p.http); |
| C1: Curl_free_request_state calls Curl_safefree(data->req.p.http) which points into the http_connect_state allocation during proxy CONNECT | |
| C2: Curl_free_request_state is a cleanup function called from Curl_close | |
| 2260 | Curl_safefree(data->req.newurl); |
| 2261 | |
| 2262 | #ifndef CURL_DISABLE_DOH |
| 2263 | if(data->req.doh) { |
| 2264 | Curl_close(&data->req.doh->probe[0].easy); |
| 2265 | Curl_close(&data->req.doh->probe[1].easy); |
| 2266 | } |
| 2267 | #endif |
| 2268 | } |
| 2269 | |
| 2270 | |
| 2271 | #ifndef CURL_DISABLE_PROXY |
| 2272 | /**************************************************************** |
| 2273 | * Checks if the host is in the noproxy list. returns true if it matches |
| 2274 | * and therefore the proxy should NOT be used. |
| 2275 | ****************************************************************/ |
| 2276 | static bool check_noproxy(const char *name, const char *no_proxy) |
| 2277 | { |
| 2278 | /* no_proxy=domain1.dom,host.domain2.dom |
| 2279 | * (a comma-separated list of hosts which should |
| 2280 | * not be proxied, or an asterisk to override |
| 2281 | * all proxy variables) |
| 2282 | */ |
| 2283 | if(no_proxy && no_proxy[0]) { |
| 2284 | size_t tok_start; |
| 783 | static void conn_free(struct connectdata *conn) |
| 784 | { |
| 785 | DEBUGASSERT(conn); |
| 786 | |
| 787 | Curl_free_idnconverted_hostname(&conn->host); |
| 788 | Curl_free_idnconverted_hostname(&conn->conn_to_host); |
| 789 | #ifndef CURL_DISABLE_PROXY |
| 790 | Curl_free_idnconverted_hostname(&conn->http_proxy.host); |
| 791 | Curl_free_idnconverted_hostname(&conn->socks_proxy.host); |
| 792 | Curl_safefree(conn->http_proxy.user); |
| 793 | Curl_safefree(conn->socks_proxy.user); |
| 794 | Curl_safefree(conn->http_proxy.passwd); |
| 795 | Curl_safefree(conn->socks_proxy.passwd); |
| 796 | Curl_safefree(conn->http_proxy.host.rawalloc); /* http proxy name buffer */ |
| 797 | Curl_safefree(conn->socks_proxy.host.rawalloc); /* socks proxy name buffer */ |
| 798 | Curl_free_primary_ssl_config(&conn->proxy_ssl_config); |
| 799 | #endif |
| 800 | Curl_safefree(conn->user); |
| 801 | Curl_safefree(conn->passwd); |
| 802 | Curl_safefree(conn->sasl_authzid); |
| 803 | Curl_safefree(conn->options); |
| 804 | Curl_safefree(conn->oauth_bearer); |
| 805 | Curl_dyn_free(&conn->trailer); |
| 806 | Curl_safefree(conn->host.rawalloc); /* host name buffer */ |
| 807 | Curl_safefree(conn->conn_to_host.rawalloc); /* host name buffer */ |
| 808 | Curl_safefree(conn->hostname_resolve); |
| 809 | Curl_safefree(conn->secondaryhostname); |
| 810 | Curl_safefree(conn->connect_state); |
| C1: conn_free calls Curl_safefree(conn->connect_state) freeing the original allocation | |
| C2: conn_free is called from Curl_disconnect during connection cleanup | |
| 811 | |
| 812 | conn_reset_all_postponed_data(conn); |
| 813 | Curl_llist_destroy(&conn->easyq, NULL); |
| 814 | Curl_safefree(conn->localdev); |
| 815 | Curl_free_primary_ssl_config(&conn->ssl_config); |
| 816 | |
| 817 | #ifdef USE_UNIX_SOCKETS |
| 818 | Curl_safefree(conn->unix_domain_socket); |
| 819 | #endif |
| 820 | |
| 821 | #ifdef USE_SSL |
| 822 | Curl_safefree(conn->ssl_extra); |
| 823 | #endif |
| 824 | free(conn); /* free all the connection oriented data */ |
| 825 | } |
| 158 | static CURLcode connect_init(struct Curl_easy *data, bool reinit) |
| C2: connect_init is the HTTP proxy CONNECT initialization function | |
| 159 | { |
| 160 | struct http_connect_state *s; |
| 161 | struct connectdata *conn = data->conn; |
| 162 | if(conn->handler->flags & PROTOPT_NOTCPPROXY) { |
| C3: connect_init checks PROTOPT_NOTCPPROXY flag - dict protocol would not have this flag | |
| 163 | failf(data, "%s cannot be done over CONNECT", conn->handler->scheme); |
| 164 | return CURLE_UNSUPPORTED_PROTOCOL; |
| 165 | } |
| 166 | if(!reinit) { |
| 167 | CURLcode result; |
| 168 | DEBUGASSERT(!conn->connect_state); |
| 169 | /* we might need the upload buffer for streaming a partial request */ |
| 170 | result = Curl_get_upload_buffer(data); |
| 171 | if(result) |
| 172 | return result; |
| 173 | |
| 174 | s = calloc(1, sizeof(struct http_connect_state)); |
| C1: connect_init allocates http_connect_state with calloc and stores in conn->connect_state | |
| C3: Allocation occurs when reinit is false, which happens for the initial proxy CONNECT | |
| 175 | if(!s) |
| 176 | return CURLE_OUT_OF_MEMORY; |
| 177 | infof(data, "allocate connect buffer"); |
| 178 | conn->connect_state = s; |
| 179 | Curl_dyn_init(&s->rcvbuf, DYN_PROXY_CONNECT_HEADERS); |
| 180 | |
| 181 | /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the |
| 182 | * member conn->proto.http; we want [protocol] through HTTP and we have |
| 183 | * to change the member temporarily for connecting to the HTTP |
| 184 | * proxy. After Curl_proxyCONNECT we have to set back the member to the |
| 185 | * original pointer |
| 186 | * |
| 187 | * This function might be called several times in the multi interface case |
| 188 | * if the proxy's CONNECT response is not instant. |
| 189 | */ |
| 190 | s->prot_save = data->req.p.http; |
| 191 | data->req.p.http = &s->http_proxy; |
| C1: data->req.p.http = &s->http_proxy makes req.p.http point into the middle of the allocation | |
| 192 | connkeep(conn, "HTTP proxy CONNECT"); |
| 193 | } |
| 194 | else { |
| 195 | DEBUGASSERT(conn->connect_state); |
| 196 | s = conn->connect_state; |
| 197 | Curl_dyn_reset(&s->rcvbuf); |
| 198 | } |
| 199 | s->tunnel_state = TUNNEL_INIT; |
| 200 | s->keepon = KEEPON_CONNECT; |
| 201 | s->cl = 0; |
| 202 | s->close_connection = FALSE; |
| 203 | return CURLE_OK; |
| 204 | } |
Show 1 lines (not cited by the judge)
| 132 | #define free(ptr) Curl_cfree(ptr) |
Show 119 lines (not cited by the judge)
| 379 | CURLcode Curl_close(struct Curl_easy **datap) |
| 380 | { |
| 381 | struct Curl_multi *m; |
| 382 | struct Curl_easy *data; |
| 383 | |
| 384 | if(!datap || !*datap) |
| 385 | return CURLE_OK; |
| 386 | |
| 387 | data = *datap; |
| 388 | *datap = NULL; |
| 389 | |
| 390 | Curl_expire_clear(data); /* shut off timers */ |
| 391 | |
| 392 | /* Detach connection if any is left. This should not be normal, but can be |
| 393 | the case for example with CONNECT_ONLY + recv/send (test 556) */ |
| 394 | Curl_detach_connection(data); |
| 395 | m = data->multi; |
| 396 | if(m) |
| 397 | /* This handle is still part of a multi handle, take care of this first |
| 398 | and detach this handle from there. */ |
| 399 | curl_multi_remove_handle(data->multi, data); |
| 400 | |
| 401 | if(data->multi_easy) { |
| 402 | /* when curl_easy_perform() is used, it creates its own multi handle to |
| 403 | use and this is the one */ |
| 404 | curl_multi_cleanup(data->multi_easy); |
| 405 | data->multi_easy = NULL; |
| 406 | } |
| 407 | |
| 408 | /* Destroy the timeout list that is held in the easy handle. It is |
| 409 | /normally/ done by curl_multi_remove_handle() but this is "just in |
| 410 | case" */ |
| 411 | Curl_llist_destroy(&data->state.timeoutlist, NULL); |
| 412 | |
| 413 | data->magic = 0; /* force a clear AFTER the possibly enforced removal from |
| 414 | the multi handle, since that function uses the magic |
| 415 | field! */ |
| 416 | |
| 417 | if(data->state.rangestringalloc) |
| 418 | free(data->state.range); |
| 419 | |
| 420 | /* freed here just in case DONE wasn't called */ |
| 421 | Curl_free_request_state(data); |
| 422 | |
| 423 | /* Close down all open SSL info and sessions */ |
| 424 | Curl_ssl_close_all(data); |
| 425 | Curl_safefree(data->state.first_host); |
| 426 | Curl_safefree(data->state.scratch); |
| 427 | Curl_ssl_free_certinfo(data); |
| 428 | |
| 429 | /* Cleanup possible redirect junk */ |
| 430 | free(data->req.newurl); |
| 431 | data->req.newurl = NULL; |
| 432 | |
| 433 | if(data->state.referer_alloc) { |
| 434 | Curl_safefree(data->state.referer); |
| 435 | data->state.referer_alloc = FALSE; |
| 436 | } |
| 437 | data->state.referer = NULL; |
| 438 | |
| 439 | up_free(data); |
| 440 | Curl_safefree(data->state.buffer); |
| 441 | Curl_dyn_free(&data->state.headerb); |
| 442 | Curl_safefree(data->state.ulbuf); |
| 443 | Curl_flush_cookies(data, TRUE); |
| 444 | Curl_altsvc_save(data, data->asi, data->set.str[STRING_ALTSVC]); |
| 445 | Curl_altsvc_cleanup(&data->asi); |
| 446 | Curl_hsts_save(data, data->hsts, data->set.str[STRING_HSTS]); |
| 447 | Curl_hsts_cleanup(&data->hsts); |
| 448 | #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH) |
| 449 | Curl_http_auth_cleanup_digest(data); |
| 450 | #endif |
| 451 | Curl_safefree(data->info.contenttype); |
| 452 | Curl_safefree(data->info.wouldredirect); |
| 453 | |
| 454 | /* this destroys the channel and we cannot use it anymore after this */ |
| 455 | Curl_resolver_cancel(data); |
| 456 | Curl_resolver_cleanup(data->state.async.resolver); |
| 457 | |
| 458 | Curl_http2_cleanup_dependencies(data); |
| 459 | |
| 460 | /* No longer a dirty share, if it exists */ |
| 461 | if(data->share) { |
| 462 | Curl_share_lock(data, CURL_LOCK_DATA_SHARE, CURL_LOCK_ACCESS_SINGLE); |
| 463 | data->share->dirty--; |
| 464 | Curl_share_unlock(data, CURL_LOCK_DATA_SHARE); |
| 465 | } |
| 466 | |
| 467 | Curl_safefree(data->state.aptr.proxyuserpwd); |
| 468 | Curl_safefree(data->state.aptr.uagent); |
| 469 | Curl_safefree(data->state.aptr.userpwd); |
| 470 | Curl_safefree(data->state.aptr.accept_encoding); |
| 471 | Curl_safefree(data->state.aptr.te); |
| 472 | Curl_safefree(data->state.aptr.rangeline); |
| 473 | Curl_safefree(data->state.aptr.ref); |
| 474 | Curl_safefree(data->state.aptr.host); |
| 475 | Curl_safefree(data->state.aptr.cookiehost); |
| 476 | Curl_safefree(data->state.aptr.rtsp_transport); |
| 477 | Curl_safefree(data->state.aptr.user); |
| 478 | Curl_safefree(data->state.aptr.passwd); |
| 479 | Curl_safefree(data->state.aptr.proxyuser); |
| 480 | Curl_safefree(data->state.aptr.proxypasswd); |
| 481 | |
| 482 | #ifndef CURL_DISABLE_DOH |
| 483 | if(data->req.doh) { |
| 484 | Curl_dyn_free(&data->req.doh->probe[0].serverdoh); |
| 485 | Curl_dyn_free(&data->req.doh->probe[1].serverdoh); |
| 486 | curl_slist_free_all(data->req.doh->headers); |
| 487 | Curl_safefree(data->req.doh); |
| 488 | } |
| 489 | #endif |
| 490 | |
| 491 | /* destruct wildcard structures if it is needed */ |
| 492 | Curl_wildcard_dtor(&data->wildcard); |
| 493 | Curl_freeset(data); |
| 494 | Curl_headers_cleanup(data); |
| 495 | free(data); |
| 496 | return CURLE_OK; |
| 497 | } |
Show 120 lines (not cited by the judge)
| 705 | static CURLcode easy_perform(struct Curl_easy *data, bool events) |
| 706 | { |
| 707 | struct Curl_multi *multi; |
| 708 | CURLMcode mcode; |
| 709 | CURLcode result = CURLE_OK; |
| 710 | SIGPIPE_VARIABLE(pipe_st); |
| 711 | |
| 712 | if(!data) |
| 713 | return CURLE_BAD_FUNCTION_ARGUMENT; |
| 714 | |
| 715 | if(data->set.errorbuffer) |
| 716 | /* clear this as early as possible */ |
| 717 | data->set.errorbuffer[0] = 0; |
| 718 | |
| 719 | if(data->multi) { |
| 720 | failf(data, "easy handle already used in multi handle"); |
| 721 | return CURLE_FAILED_INIT; |
| 722 | } |
| 723 | |
| 724 | if(data->multi_easy) |
| 725 | multi = data->multi_easy; |
| 726 | else { |
| 727 | /* this multi handle will only ever have a single easy handled attached |
| 728 | to it, so make it use minimal hashes */ |
| 729 | multi = Curl_multi_handle(1, 3, 7); |
| 730 | if(!multi) |
| 731 | return CURLE_OUT_OF_MEMORY; |
| 732 | data->multi_easy = multi; |
| 733 | } |
| 734 | |
| 735 | if(multi->in_callback) |
| 736 | return CURLE_RECURSIVE_API_CALL; |
| 737 | |
| 738 | /* Copy the MAXCONNECTS option to the multi handle */ |
| 739 | curl_multi_setopt(multi, CURLMOPT_MAXCONNECTS, data->set.maxconnects); |
| 740 | |
| 741 | mcode = curl_multi_add_handle(multi, data); |
| 742 | if(mcode) { |
| 743 | curl_multi_cleanup(multi); |
| 744 | data->multi_easy = NULL; |
| 745 | if(mcode == CURLM_OUT_OF_MEMORY) |
| 746 | return CURLE_OUT_OF_MEMORY; |
| 747 | return CURLE_FAILED_INIT; |
| 748 | } |
| 749 | |
| 750 | sigpipe_ignore(data, &pipe_st); |
| 751 | |
| 752 | /* run the transfer */ |
| 753 | result = events ? easy_events(multi) : easy_transfer(multi); |
| 754 | |
| 755 | /* ignoring the return code isn't nice, but atm we can't really handle |
| 756 | a failure here, room for future improvement! */ |
| 757 | (void)curl_multi_remove_handle(multi, data); |
| 758 | |
| 759 | sigpipe_restore(&pipe_st); |
| 760 | |
| 761 | /* The multi handle is kept alive, owned by the easy handle */ |
| 762 | return result; |
| 763 | } |
| 764 | |
| 765 | |
| 766 | /* |
| 767 | * curl_easy_perform() is the external interface that performs a blocking |
| 768 | * transfer as previously setup. |
| 769 | */ |
| 770 | CURLcode curl_easy_perform(struct Curl_easy *data) |
| 771 | { |
| 772 | return easy_perform(data, FALSE); |
| 773 | } |
| 774 | |
| 775 | #ifdef CURLDEBUG |
| 776 | /* |
| 777 | * curl_easy_perform_ev() is the external interface that performs a blocking |
| 778 | * transfer using the event-based API internally. |
| 779 | */ |
| 780 | CURLcode curl_easy_perform_ev(struct Curl_easy *data) |
| 781 | { |
| 782 | return easy_perform(data, TRUE); |
| 783 | } |
| 784 | |
| 785 | #endif |
| 786 | |
| 787 | /* |
| 788 | * curl_easy_cleanup() is the external interface to cleaning/freeing the given |
| 789 | * easy handle. |
| 790 | */ |
| 791 | void curl_easy_cleanup(struct Curl_easy *data) |
| 792 | { |
| 793 | SIGPIPE_VARIABLE(pipe_st); |
| 794 | |
| 795 | if(!data) |
| 796 | return; |
| 797 | |
| 798 | sigpipe_ignore(data, &pipe_st); |
| 799 | Curl_close(&data); |
| 800 | sigpipe_restore(&pipe_st); |
| 801 | } |
| 802 | |
| 803 | /* |
| 804 | * curl_easy_getinfo() is an external interface that allows an app to retrieve |
| 805 | * information from a performed transfer and similar. |
| 806 | */ |
| 807 | #undef curl_easy_getinfo |
| 808 | CURLcode curl_easy_getinfo(struct Curl_easy *data, CURLINFO info, ...) |
| 809 | { |
| 810 | va_list arg; |
| 811 | void *paramp; |
| 812 | CURLcode result; |
| 813 | |
| 814 | va_start(arg, info); |
| 815 | paramp = va_arg(arg, void *); |
| 816 | |
| 817 | result = Curl_getinfo(data, info, paramp); |
| 818 | |
| 819 | va_end(arg); |
| 820 | return result; |
| 821 | } |
| 822 | |
| 823 | static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src) |
| 824 | { |
Show 51 lines (not cited by the judge)
| 632 | /* File time can only be set _after_ the file has been closed */ |
| 633 | if(!result && config->remote_time && outs->s_isreg && outs->filename) { |
| 634 | /* Ask libcurl if we got a remote file time */ |
| 635 | curl_off_t filetime = -1; |
| 636 | curl_easy_getinfo(curl, CURLINFO_FILETIME_T, &filetime); |
| 637 | setfiletime(filetime, outs->filename, global); |
| 638 | } |
| 639 | |
| 640 | /* Write the --write-out data before cleanup but after result is final */ |
| 641 | if(config->writeout) |
| 642 | ourWriteOut(config->writeout, per, result); |
| 643 | |
| 644 | /* Close function-local opened file descriptors */ |
| 645 | if(per->heads.fopened && per->heads.stream) |
| 646 | fclose(per->heads.stream); |
| 647 | |
| 648 | if(per->heads.alloc_filename) |
| 649 | Curl_safefree(per->heads.filename); |
| 650 | |
| 651 | if(per->etag_save.fopened && per->etag_save.stream) |
| 652 | fclose(per->etag_save.stream); |
| 653 | |
| 654 | if(per->etag_save.alloc_filename) |
| 655 | Curl_safefree(per->etag_save.filename); |
| 656 | |
| 657 | curl_easy_cleanup(per->curl); |
| 658 | if(outs->alloc_filename) |
| 659 | free(outs->filename); |
| 660 | free(per->this_url); |
| 661 | free(per->outfile); |
| 662 | free(per->uploadfile); |
| 663 | if(global->parallel) |
| 664 | free(per->errorbuffer); |
| 665 | |
| 666 | return result; |
| 667 | } |
| 668 | |
| 669 | static void single_transfer_cleanup(struct OperationConfig *config) |
| 670 | { |
| 671 | if(config) { |
| 672 | struct State *state = &config->state; |
| 673 | if(state->urls) { |
| 674 | /* Free list of remaining URLs */ |
| 675 | glob_cleanup(state->urls); |
| 676 | state->urls = NULL; |
| 677 | } |
| 678 | Curl_safefree(state->outfiles); |
| 679 | Curl_safefree(state->httpgetfields); |
| 680 | Curl_safefree(state->uploadfile); |
| 681 | if(state->inglob) { |
| 682 | /* Free list of globbed upload files */ |
Show 80 lines (not cited by the judge)
| 2393 | static CURLcode serial_transfers(struct GlobalConfig *global, |
| 2394 | CURLSH *share) |
| 2395 | { |
| 2396 | CURLcode returncode = CURLE_OK; |
| 2397 | CURLcode result = CURLE_OK; |
| 2398 | struct per_transfer *per; |
| 2399 | bool added = FALSE; |
| 2400 | |
| 2401 | result = create_transfer(global, share, &added); |
| 2402 | if(result) |
| 2403 | return result; |
| 2404 | if(!added) { |
| 2405 | errorf(global, "no transfer performed\n"); |
| 2406 | return CURLE_READ_ERROR; |
| 2407 | } |
| 2408 | for(per = transfers; per;) { |
| 2409 | bool retry; |
| 2410 | long delay_ms; |
| 2411 | bool bailout = FALSE; |
| 2412 | struct timeval start; |
| 2413 | result = pre_transfer(global, per); |
| 2414 | if(result) |
| 2415 | break; |
| 2416 | |
| 2417 | if(global->libcurl) { |
| 2418 | result = easysrc_perform(); |
| 2419 | if(result) |
| 2420 | break; |
| 2421 | } |
| 2422 | |
| 2423 | start = tvnow(); |
| 2424 | #ifdef CURLDEBUG |
| 2425 | if(global->test_event_based) |
| 2426 | result = curl_easy_perform_ev(per->curl); |
| 2427 | else |
| 2428 | #endif |
| 2429 | result = curl_easy_perform(per->curl); |
| 2430 | |
| 2431 | returncode = post_per_transfer(global, per, result, &retry, &delay_ms); |
| 2432 | if(retry) { |
| 2433 | tool_go_sleep(delay_ms); |
| 2434 | continue; |
| 2435 | } |
| 2436 | |
| 2437 | /* Bail out upon critical errors or --fail-early */ |
| 2438 | if(is_fatal_error(returncode) || (returncode && global->fail_early)) |
| 2439 | bailout = TRUE; |
| 2440 | else { |
| 2441 | /* setup the next one just before we delete this */ |
| 2442 | result = create_transfer(global, share, &added); |
| 2443 | if(result) |
| 2444 | bailout = TRUE; |
| 2445 | } |
| 2446 | |
| 2447 | per = del_per_transfer(per); |
| 2448 | |
| 2449 | if(bailout) |
| 2450 | break; |
| 2451 | |
| 2452 | if(per && global->ms_per_transfer) { |
| 2453 | /* how long time did the most recent transfer take in number of |
| 2454 | milliseconds */ |
| 2455 | long milli = tvdiff(tvnow(), start); |
| 2456 | if(milli < global->ms_per_transfer) { |
| 2457 | notef(global, "Transfer took %ld ms, waits %ldms as set by --rate\n", |
| 2458 | milli, global->ms_per_transfer - milli); |
| 2459 | /* The transfer took less time than wanted. Wait a little. */ |
| 2460 | tool_go_sleep(global->ms_per_transfer - milli); |
| 2461 | } |
| 2462 | } |
| 2463 | } |
| 2464 | if(returncode) |
| 2465 | /* returncode errors have priority */ |
| 2466 | result = returncode; |
| 2467 | |
| 2468 | if(result) |
| 2469 | single_transfer_cleanup(global->current); |
| 2470 | |
| 2471 | return result; |
| 2472 | } |
Show 51 lines (not cited by the judge)
| 2592 | result = transfer_per_config(global, global->current, share, added); |
| 2593 | if(!result && !*added) { |
| 2594 | /* when one set is drained, continue to next */ |
| 2595 | global->current = global->current->next; |
| 2596 | continue; |
| 2597 | } |
| 2598 | break; |
| 2599 | } |
| 2600 | return result; |
| 2601 | } |
| 2602 | |
| 2603 | static CURLcode run_all_transfers(struct GlobalConfig *global, |
| 2604 | CURLSH *share, |
| 2605 | CURLcode result) |
| 2606 | { |
| 2607 | /* Save the values of noprogress and isatty to restore them later on */ |
| 2608 | bool orig_noprogress = global->noprogress; |
| 2609 | bool orig_isatty = global->isatty; |
| 2610 | struct per_transfer *per; |
| 2611 | |
| 2612 | /* Time to actually do the transfers */ |
| 2613 | if(!result) { |
| 2614 | if(global->parallel) |
| 2615 | result = parallel_transfers(global, share); |
| 2616 | else |
| 2617 | result = serial_transfers(global, share); |
| 2618 | } |
| 2619 | |
| 2620 | /* cleanup if there are any left */ |
| 2621 | for(per = transfers; per;) { |
| 2622 | bool retry; |
| 2623 | long delay; |
| 2624 | CURLcode result2 = post_per_transfer(global, per, result, &retry, &delay); |
| 2625 | if(!result) |
| 2626 | /* don't overwrite the original error */ |
| 2627 | result = result2; |
| 2628 | |
| 2629 | /* Free list of given URLs */ |
| 2630 | clean_getout(per->config); |
| 2631 | |
| 2632 | per = del_per_transfer(per); |
| 2633 | } |
| 2634 | |
| 2635 | /* Reset the global config variables */ |
| 2636 | global->noprogress = orig_noprogress; |
| 2637 | global->isatty = orig_isatty; |
| 2638 | |
| 2639 | |
| 2640 | return result; |
| 2641 | } |
| 2642 |
Show 43 lines (not cited by the judge)
| 2704 | if(!share) { |
| 2705 | if(global->libcurl) { |
| 2706 | /* Cleanup the libcurl source output */ |
| 2707 | easysrc_cleanup(); |
| 2708 | } |
| 2709 | return CURLE_OUT_OF_MEMORY; |
| 2710 | } |
| 2711 | |
| 2712 | curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_COOKIE); |
| 2713 | curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS); |
| 2714 | curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_SSL_SESSION); |
| 2715 | curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT); |
| 2716 | curl_share_setopt(share, CURLSHOPT_SHARE, CURL_LOCK_DATA_PSL); |
| 2717 | |
| 2718 | /* Get the required arguments for each operation */ |
| 2719 | do { |
| 2720 | result = get_args(operation, count++); |
| 2721 | |
| 2722 | operation = operation->next; |
| 2723 | } while(!result && operation); |
| 2724 | |
| 2725 | /* Set the current operation pointer */ |
| 2726 | global->current = global->first; |
| 2727 | |
| 2728 | /* now run! */ |
| 2729 | result = run_all_transfers(global, share, result); |
| 2730 | |
| 2731 | curl_share_cleanup(share); |
| 2732 | if(global->libcurl) { |
| 2733 | /* Cleanup the libcurl source output */ |
| 2734 | easysrc_cleanup(); |
| 2735 | |
| 2736 | /* Dump the libcurl code if previously enabled */ |
| 2737 | dumpeasysrc(global); |
| 2738 | } |
| 2739 | } |
| 2740 | else |
| 2741 | errorf(global, "out of memory\n"); |
| 2742 | } |
| 2743 | } |
| 2744 | |
| 2745 | return result; |
| 2746 | } |
Show 78 lines (not cited by the judge)
| 841 | void Curl_disconnect(struct Curl_easy *data, |
| 842 | struct connectdata *conn, bool dead_connection) |
| 843 | { |
| 844 | /* there must be a connection to close */ |
| 845 | DEBUGASSERT(conn); |
| 846 | |
| 847 | /* it must be removed from the connection cache */ |
| 848 | DEBUGASSERT(!conn->bundle); |
| 849 | |
| 850 | /* there must be an associated transfer */ |
| 851 | DEBUGASSERT(data); |
| 852 | |
| 853 | /* the transfer must be detached from the connection */ |
| 854 | DEBUGASSERT(!data->conn); |
| 855 | |
| 856 | /* |
| 857 | * If this connection isn't marked to force-close, leave it open if there |
| 858 | * are other users of it |
| 859 | */ |
| 860 | if(CONN_INUSE(conn) && !dead_connection) { |
| 861 | DEBUGF(infof(data, "Curl_disconnect when inuse: %zu", CONN_INUSE(conn))); |
| 862 | return; |
| 863 | } |
| 864 | |
| 865 | if(conn->dns_entry) { |
| 866 | Curl_resolv_unlock(data, conn->dns_entry); |
| 867 | conn->dns_entry = NULL; |
| 868 | } |
| 869 | |
| 870 | /* Cleanup NTLM connection-related data */ |
| 871 | Curl_http_auth_cleanup_ntlm(conn); |
| 872 | |
| 873 | /* Cleanup NEGOTIATE connection-related data */ |
| 874 | Curl_http_auth_cleanup_negotiate(conn); |
| 875 | |
| 876 | if(conn->connect_only) |
| 877 | /* treat the connection as dead in CONNECT_ONLY situations */ |
| 878 | dead_connection = TRUE; |
| 879 | |
| 880 | /* temporarily attach the connection to this transfer handle for the |
| 881 | disconnect and shutdown */ |
| 882 | Curl_attach_connection(data, conn); |
| 883 | |
| 884 | if(conn->handler->disconnect) |
| 885 | /* This is set if protocol-specific cleanups should be made */ |
| 886 | conn->handler->disconnect(data, conn, dead_connection); |
| 887 | |
| 888 | conn_shutdown(data, conn); |
| 889 | |
| 890 | /* detach it again */ |
| 891 | Curl_detach_connection(data); |
| 892 | |
| 893 | conn_free(conn); |
| 894 | } |
| 895 | |
| 896 | /* |
| 897 | * This function should return TRUE if the socket is to be assumed to |
| 898 | * be dead. Most commonly this happens when the server has closed the |
| 899 | * connection due to inactivity. |
| 900 | */ |
| 901 | static bool SocketIsDead(curl_socket_t sock) |
| 902 | { |
| 903 | int sval; |
| 904 | bool ret_val = TRUE; |
| 905 | |
| 906 | sval = SOCKET_READABLE(sock, 0); |
| 907 | if(sval == 0) |
| 908 | /* timeout */ |
| 909 | ret_val = FALSE; |
| 910 | |
| 911 | return ret_val; |
| 912 | } |
| 913 | |
| 914 | /* |
| 915 | * IsMultiplexingPossible() |
| 916 | * |
| 917 | * Return a bitmask with the available multiplexing options for the given |
| 918 | * requested connection. |
Show 51 lines (not cited by the judge)
| 2589 | * If an error was returned, and we aren't in completed state now, |
| 2590 | * then we go to completed and consider this transfer aborted. |
| 2591 | */ |
| 2592 | |
| 2593 | /* NOTE: no attempt to disconnect connections must be made |
| 2594 | in the case blocks above - cleanup happens only here */ |
| 2595 | |
| 2596 | /* Check if we can move pending requests to send pipe */ |
| 2597 | process_pending_handles(multi); /* connection */ |
| 2598 | |
| 2599 | if(data->conn) { |
| 2600 | if(stream_error) { |
| 2601 | /* Don't attempt to send data over a connection that timed out */ |
| 2602 | bool dead_connection = result == CURLE_OPERATION_TIMEDOUT; |
| 2603 | struct connectdata *conn = data->conn; |
| 2604 | |
| 2605 | /* This is where we make sure that the conn pointer is reset. |
| 2606 | We don't have to do this in every case block above where a |
| 2607 | failure is detected */ |
| 2608 | Curl_detach_connection(data); |
| 2609 | |
| 2610 | /* remove connection from cache */ |
| 2611 | Curl_conncache_remove_conn(data, conn, TRUE); |
| 2612 | |
| 2613 | /* disconnect properly */ |
| 2614 | Curl_disconnect(data, conn, dead_connection); |
| 2615 | } |
| 2616 | } |
| 2617 | else if(data->mstate == MSTATE_CONNECT) { |
| 2618 | /* Curl_connect() failed */ |
| 2619 | (void)Curl_posttransfer(data); |
| 2620 | } |
| 2621 | |
| 2622 | multistate(data, MSTATE_COMPLETED); |
| 2623 | rc = CURLM_CALL_MULTI_PERFORM; |
| 2624 | } |
| 2625 | /* if there's still a connection to use, call the progress function */ |
| 2626 | else if(data->conn && Curl_pgrsUpdate(data)) { |
| 2627 | /* aborted due to progress callback return code must close the |
| 2628 | connection */ |
| 2629 | result = CURLE_ABORTED_BY_CALLBACK; |
| 2630 | streamclose(data->conn, "Aborted by callback"); |
| 2631 | |
| 2632 | /* if not yet in DONE state, go there, otherwise COMPLETED */ |
| 2633 | multistate(data, (data->mstate < MSTATE_DONE)? |
| 2634 | MSTATE_DONE: MSTATE_COMPLETED); |
| 2635 | rc = CURLM_CALL_MULTI_PERFORM; |
| 2636 | } |
| 2637 | } |
| 2638 | |
| 2639 | if(MSTATE_COMPLETED == data->mstate) { |
Show 59 lines (not cited by the judge)
| 2658 | |
| 2659 | data->result = result; |
| 2660 | return rc; |
| 2661 | } |
| 2662 | |
| 2663 | |
| 2664 | CURLMcode curl_multi_perform(struct Curl_multi *multi, int *running_handles) |
| 2665 | { |
| 2666 | struct Curl_easy *data; |
| 2667 | CURLMcode returncode = CURLM_OK; |
| 2668 | struct Curl_tree *t; |
| 2669 | struct curltime now = Curl_now(); |
| 2670 | |
| 2671 | if(!GOOD_MULTI_HANDLE(multi)) |
| 2672 | return CURLM_BAD_HANDLE; |
| 2673 | |
| 2674 | if(multi->in_callback) |
| 2675 | return CURLM_RECURSIVE_API_CALL; |
| 2676 | |
| 2677 | data = multi->easyp; |
| 2678 | while(data) { |
| 2679 | CURLMcode result; |
| 2680 | SIGPIPE_VARIABLE(pipe_st); |
| 2681 | |
| 2682 | sigpipe_ignore(data, &pipe_st); |
| 2683 | result = multi_runsingle(multi, &now, data); |
| 2684 | sigpipe_restore(&pipe_st); |
| 2685 | |
| 2686 | if(result) |
| 2687 | returncode = result; |
| 2688 | |
| 2689 | data = data->next; /* operate on next handle */ |
| 2690 | } |
| 2691 | |
| 2692 | /* |
| 2693 | * Simply remove all expired timers from the splay since handles are dealt |
| 2694 | * with unconditionally by this function and curl_multi_timeout() requires |
| 2695 | * that already passed/handled expire times are removed from the splay. |
| 2696 | * |
| 2697 | * It is important that the 'now' value is set at the entry of this function |
| 2698 | * and not for the current time as it may have ticked a little while since |
| 2699 | * then and then we risk this loop to remove timers that actually have not |
| 2700 | * been handled! |
| 2701 | */ |
| 2702 | do { |
| 2703 | multi->timetree = Curl_splaygetbest(now, multi->timetree, &t); |
| 2704 | if(t) |
| 2705 | /* the removed may have another timeout in queue */ |
| 2706 | (void)add_next_timeout(now, multi, t->payload); |
| 2707 | |
| 2708 | } while(t); |
| 2709 | |
| 2710 | *running_handles = multi->num_alive; |
| 2711 | |
| 2712 | if(CURLM_OK >= returncode) |
| 2713 | returncode = Curl_update_timer(multi); |
| 2714 | |
| 2715 | return returncode; |
| 2716 | } |
Show 51 lines (not cited by the judge)
| 638 | returns and curl_multi_remove_handle() is called */ |
| 639 | static struct events evs = {2, FALSE, 0, NULL, 0}; |
| 640 | |
| 641 | /* if running event-based, do some further multi inits */ |
| 642 | events_setup(multi, &evs); |
| 643 | |
| 644 | return wait_or_timeout(multi, &evs); |
| 645 | } |
| 646 | #else /* CURLDEBUG */ |
| 647 | /* when not built with debug, this function doesn't exist */ |
| 648 | #define easy_events(x) CURLE_NOT_BUILT_IN |
| 649 | #endif |
| 650 | |
| 651 | static CURLcode easy_transfer(struct Curl_multi *multi) |
| 652 | { |
| 653 | bool done = FALSE; |
| 654 | CURLMcode mcode = CURLM_OK; |
| 655 | CURLcode result = CURLE_OK; |
| 656 | |
| 657 | while(!done && !mcode) { |
| 658 | int still_running = 0; |
| 659 | |
| 660 | mcode = curl_multi_poll(multi, NULL, 0, 1000, NULL); |
| 661 | |
| 662 | if(!mcode) |
| 663 | mcode = curl_multi_perform(multi, &still_running); |
| 664 | |
| 665 | /* only read 'still_running' if curl_multi_perform() return OK */ |
| 666 | if(!mcode && !still_running) { |
| 667 | int rc; |
| 668 | CURLMsg *msg = curl_multi_info_read(multi, &rc); |
| 669 | if(msg) { |
| 670 | result = msg->data.result; |
| 671 | done = TRUE; |
| 672 | } |
| 673 | } |
| 674 | } |
| 675 | |
| 676 | /* Make sure to return some kind of error if there was a multi problem */ |
| 677 | if(mcode) { |
| 678 | result = (mcode == CURLM_OUT_OF_MEMORY) ? CURLE_OUT_OF_MEMORY : |
| 679 | /* The other multi errors should never happen, so return |
| 680 | something suitably generic */ |
| 681 | CURLE_BAD_FUNCTION_ARGUMENT; |
| 682 | } |
| 683 | |
| 684 | return result; |
| 685 | } |
| 686 | |
| 687 | |
| 688 | /* |
Show 44 lines (not cited by the judge)
| 1036 | |
| 1037 | void Curl_connect_free(struct Curl_easy *data) |
| 1038 | { |
| 1039 | struct connectdata *conn = data->conn; |
| 1040 | struct http_connect_state *s = conn->connect_state; |
| 1041 | if(s) { |
| 1042 | free(s); |
| 1043 | conn->connect_state = NULL; |
| 1044 | } |
| 1045 | } |
| 1046 | |
| 1047 | /* |
| 1048 | * Curl_proxyCONNECT() requires that we're connected to a HTTP proxy. This |
| 1049 | * function will issue the necessary commands to get a seamless tunnel through |
| 1050 | * this proxy. After that, the socket can be used just as a normal socket. |
| 1051 | */ |
| 1052 | |
| 1053 | CURLcode Curl_proxyCONNECT(struct Curl_easy *data, |
| 1054 | int sockindex, |
| 1055 | const char *hostname, |
| 1056 | int remote_port) |
| 1057 | { |
| 1058 | CURLcode result; |
| 1059 | struct connectdata *conn = data->conn; |
| 1060 | if(!conn->connect_state) { |
| 1061 | result = connect_init(data, FALSE); |
| 1062 | if(result) |
| 1063 | return result; |
| 1064 | } |
| 1065 | result = CONNECT(data, sockindex, hostname, remote_port); |
| 1066 | |
| 1067 | if(result || Curl_connect_complete(conn)) |
| 1068 | Curl_connect_done(data); |
| 1069 | |
| 1070 | return result; |
| 1071 | } |
| 1072 | |
| 1073 | #else |
| 1074 | void Curl_connect_free(struct Curl_easy *data) |
| 1075 | { |
| 1076 | (void)data; |
| 1077 | } |
| 1078 | |
| 1079 | #endif /* CURL_DISABLE_PROXY */ |
Show 64 lines (not cited by the judge)
| 80 | CURLcode Curl_proxy_connect(struct Curl_easy *data, int sockindex) |
| 81 | { |
| 82 | struct connectdata *conn = data->conn; |
| 83 | if(conn->http_proxy.proxytype == CURLPROXY_HTTPS) { |
| 84 | const CURLcode result = https_proxy_connect(data, sockindex); |
| 85 | if(result) |
| 86 | return result; |
| 87 | if(!conn->bits.proxy_ssl_connected[sockindex]) |
| 88 | return result; /* wait for HTTPS proxy SSL initialization to complete */ |
| 89 | } |
| 90 | |
| 91 | if(conn->bits.tunnel_proxy && conn->bits.httpproxy) { |
| 92 | #ifndef CURL_DISABLE_PROXY |
| 93 | /* for [protocol] tunneled through HTTP proxy */ |
| 94 | const char *hostname; |
| 95 | int remote_port; |
| 96 | CURLcode result; |
| 97 | |
| 98 | /* We want "seamless" operations through HTTP proxy tunnel */ |
| 99 | |
| 100 | /* for the secondary socket (FTP), use the "connect to host" |
| 101 | * but ignore the "connect to port" (use the secondary port) |
| 102 | */ |
| 103 | |
| 104 | if(conn->bits.conn_to_host) |
| 105 | hostname = conn->conn_to_host.name; |
| 106 | else if(sockindex == SECONDARYSOCKET) |
| 107 | hostname = conn->secondaryhostname; |
| 108 | else |
| 109 | hostname = conn->host.name; |
| 110 | |
| 111 | if(sockindex == SECONDARYSOCKET) |
| 112 | remote_port = conn->secondary_port; |
| 113 | else if(conn->bits.conn_to_port) |
| 114 | remote_port = conn->conn_to_port; |
| 115 | else |
| 116 | remote_port = conn->remote_port; |
| 117 | |
| 118 | result = Curl_proxyCONNECT(data, sockindex, hostname, remote_port); |
| 119 | if(CURLE_OK != result) |
| 120 | return result; |
| 121 | Curl_safefree(data->state.aptr.proxyuserpwd); |
| 122 | #else |
| 123 | return CURLE_NOT_BUILT_IN; |
| 124 | #endif |
| 125 | } |
| 126 | /* no HTTP tunnel proxy, just return */ |
| 127 | return CURLE_OK; |
| 128 | } |
| 129 | |
| 130 | bool Curl_connect_complete(struct connectdata *conn) |
| 131 | { |
| 132 | return !conn->connect_state || |
| 133 | (conn->connect_state->tunnel_state >= TUNNEL_COMPLETE); |
| 134 | } |
| 135 | |
| 136 | bool Curl_connect_ongoing(struct connectdata *conn) |
| 137 | { |
| 138 | return conn->connect_state && |
| 139 | (conn->connect_state->tunnel_state <= TUNNEL_COMPLETE); |
| 140 | } |
| 141 | |
| 142 | /* when we've sent a CONNECT to a proxy, we should rather either wait for the |
| 143 | socket to become readable to be able to get the response headers or if |
Show 59 lines (not cited by the judge)
| 1526 | |
| 1527 | /* find the content string in the rest of the line */ |
| 1528 | for(; len >= clen; len--, start++) { |
| 1529 | if(strncasecompare(start, content, clen)) |
| 1530 | return TRUE; /* match! */ |
| 1531 | } |
| 1532 | |
| 1533 | return FALSE; /* no match */ |
| 1534 | } |
| 1535 | |
| 1536 | /* |
| 1537 | * Curl_http_connect() performs HTTP stuff to do at connect-time, called from |
| 1538 | * the generic Curl_connect(). |
| 1539 | */ |
| 1540 | CURLcode Curl_http_connect(struct Curl_easy *data, bool *done) |
| 1541 | { |
| 1542 | CURLcode result; |
| 1543 | struct connectdata *conn = data->conn; |
| 1544 | |
| 1545 | /* We default to persistent connections. We set this already in this connect |
| 1546 | function to make the re-use checks properly be able to check this bit. */ |
| 1547 | connkeep(conn, "HTTP default"); |
| 1548 | |
| 1549 | #ifndef CURL_DISABLE_PROXY |
| 1550 | /* the CONNECT procedure might not have been completed */ |
| 1551 | result = Curl_proxy_connect(data, FIRSTSOCKET); |
| 1552 | if(result) |
| 1553 | return result; |
| 1554 | |
| 1555 | if(conn->bits.proxy_connect_closed) |
| 1556 | /* this is not an error, just part of the connection negotiation */ |
| 1557 | return CURLE_OK; |
| 1558 | |
| 1559 | if(CONNECT_FIRSTSOCKET_PROXY_SSL()) |
| 1560 | return CURLE_OK; /* wait for HTTPS proxy SSL initialization to complete */ |
| 1561 | |
| 1562 | if(Curl_connect_ongoing(conn)) |
| 1563 | /* nothing else to do except wait right now - we're not done here. */ |
| 1564 | return CURLE_OK; |
| 1565 | |
| 1566 | if(data->set.haproxyprotocol) { |
| 1567 | /* add HAProxy PROXY protocol header */ |
| 1568 | result = add_haproxy_protocol_header(data); |
| 1569 | if(result) |
| 1570 | return result; |
| 1571 | } |
| 1572 | #endif |
| 1573 | |
| 1574 | if(conn->given->flags & PROTOPT_SSL) { |
| 1575 | /* perform SSL initialization */ |
| 1576 | result = https_connecting(data, done); |
| 1577 | if(result) |
| 1578 | return result; |
| 1579 | } |
| 1580 | else |
| 1581 | *done = TRUE; |
| 1582 | |
| 1583 | return CURLE_OK; |
| 1584 | } |
Show 51 lines (not cited by the judge)
| 2002 | if(protocol_connected) |
| 2003 | multistate(data, MSTATE_DO); |
| 2004 | else { |
| 2005 | #ifndef CURL_DISABLE_HTTP |
| 2006 | if(Curl_connect_ongoing(data->conn)) |
| 2007 | multistate(data, MSTATE_TUNNELING); |
| 2008 | else |
| 2009 | #endif |
| 2010 | multistate(data, MSTATE_CONNECTING); |
| 2011 | } |
| 2012 | } |
| 2013 | } |
| 2014 | |
| 2015 | if(result) { |
| 2016 | /* failure detected */ |
| 2017 | stream_error = TRUE; |
| 2018 | break; |
| 2019 | } |
| 2020 | } |
| 2021 | break; |
| 2022 | |
| 2023 | #ifndef CURL_DISABLE_HTTP |
| 2024 | case MSTATE_TUNNELING: |
| 2025 | /* this is HTTP-specific, but sending CONNECT to a proxy is HTTP... */ |
| 2026 | DEBUGASSERT(data->conn); |
| 2027 | result = Curl_http_connect(data, &protocol_connected); |
| 2028 | #ifndef CURL_DISABLE_PROXY |
| 2029 | if(data->conn->bits.proxy_connect_closed) { |
| 2030 | rc = CURLM_CALL_MULTI_PERFORM; |
| 2031 | /* connect back to proxy again */ |
| 2032 | result = CURLE_OK; |
| 2033 | multi_done(data, CURLE_OK, FALSE); |
| 2034 | multistate(data, MSTATE_CONNECT); |
| 2035 | } |
| 2036 | else |
| 2037 | #endif |
| 2038 | if(!result) { |
| 2039 | if( |
| 2040 | #ifndef CURL_DISABLE_PROXY |
| 2041 | (data->conn->http_proxy.proxytype != CURLPROXY_HTTPS || |
| 2042 | data->conn->bits.proxy_ssl_connected[FIRSTSOCKET]) && |
| 2043 | #endif |
| 2044 | Curl_connect_complete(data->conn)) { |
| 2045 | rc = CURLM_CALL_MULTI_PERFORM; |
| 2046 | /* initiate protocol connect phase */ |
| 2047 | multistate(data, MSTATE_PROTOCONNECT); |
| 2048 | } |
| 2049 | } |
| 2050 | else |
| 2051 | stream_error = TRUE; |
| 2052 | break; |
Show 54 lines (not cited by the judge)
| 837 | void Curl_disconnect(struct Curl_easy *data, |
| 838 | struct connectdata *conn, bool dead_connection) |
| 839 | { |
| 840 | /* there must be a connection to close */ |
| 841 | DEBUGASSERT(conn); |
| 842 | |
| 843 | /* it must be removed from the connection cache */ |
| 844 | DEBUGASSERT(!conn->bundle); |
| 845 | |
| 846 | /* there must be an associated transfer */ |
| 847 | DEBUGASSERT(data); |
| 848 | |
| 849 | /* the transfer must be detached from the connection */ |
| 850 | DEBUGASSERT(!data->conn); |
| 851 | |
| 852 | /* |
| 853 | * If this connection isn't marked to force-close, leave it open if there |
| 854 | * are other users of it |
| 855 | */ |
| 856 | if(CONN_INUSE(conn) && !dead_connection) { |
| 857 | DEBUGF(infof(data, "Curl_disconnect when inuse: %zu", CONN_INUSE(conn))); |
| 858 | return; |
| 859 | } |
| 860 | |
| 861 | if(conn->dns_entry) { |
| 862 | Curl_resolv_unlock(data, conn->dns_entry); |
| 863 | conn->dns_entry = NULL; |
| 864 | } |
| 865 | |
| 866 | /* Cleanup NTLM connection-related data */ |
| 867 | Curl_http_auth_cleanup_ntlm(conn); |
| 868 | |
| 869 | /* Cleanup NEGOTIATE connection-related data */ |
| 870 | Curl_http_auth_cleanup_negotiate(conn); |
| 871 | |
| 872 | if(conn->bits.connect_only) |
| 873 | /* treat the connection as dead in CONNECT_ONLY situations */ |
| 874 | dead_connection = TRUE; |
| 875 | |
| 876 | /* temporarily attach the connection to this transfer handle for the |
| 877 | disconnect and shutdown */ |
| 878 | Curl_attach_connection(data, conn); |
| 879 | |
| 880 | if(conn->handler->disconnect) |
| 881 | /* This is set if protocol-specific cleanups should be made */ |
| 882 | conn->handler->disconnect(data, conn, dead_connection); |
| 883 | |
| 884 | conn_shutdown(data, conn); |
| 885 | |
| 886 | /* detach it again */ |
| 887 | Curl_detach_connection(data); |
| 888 | |
| 889 | conn_free(conn); |
| 890 | } |
Show 45 lines (not cited by the judge)
| 1487 | CURLcode Curl_http_connect(struct Curl_easy *data, bool *done) |
| 1488 | { |
| 1489 | CURLcode result; |
| 1490 | struct connectdata *conn = data->conn; |
| 1491 | |
| 1492 | /* We default to persistent connections. We set this already in this connect |
| 1493 | function to make the re-use checks properly be able to check this bit. */ |
| 1494 | connkeep(conn, "HTTP default"); |
| 1495 | |
| 1496 | #ifndef CURL_DISABLE_PROXY |
| 1497 | /* the CONNECT procedure might not have been completed */ |
| 1498 | result = Curl_proxy_connect(data, FIRSTSOCKET); |
| 1499 | if(result) |
| 1500 | return result; |
| 1501 | |
| 1502 | if(conn->bits.proxy_connect_closed) |
| 1503 | /* this is not an error, just part of the connection negotiation */ |
| 1504 | return CURLE_OK; |
| 1505 | |
| 1506 | if(CONNECT_FIRSTSOCKET_PROXY_SSL()) |
| 1507 | return CURLE_OK; /* wait for HTTPS proxy SSL initialization to complete */ |
| 1508 | |
| 1509 | if(Curl_connect_ongoing(conn)) |
| 1510 | /* nothing else to do except wait right now - we're not done here. */ |
| 1511 | return CURLE_OK; |
| 1512 | |
| 1513 | if(data->set.haproxyprotocol) { |
| 1514 | /* add HAProxy PROXY protocol header */ |
| 1515 | result = add_haproxy_protocol_header(data); |
| 1516 | if(result) |
| 1517 | return result; |
| 1518 | } |
| 1519 | #endif |
| 1520 | |
| 1521 | if(conn->given->protocol & CURLPROTO_HTTPS) { |
| 1522 | /* perform SSL initialization */ |
| 1523 | result = https_connecting(data, done); |
| 1524 | if(result) |
| 1525 | return result; |
| 1526 | } |
| 1527 | else |
| 1528 | *done = TRUE; |
| 1529 | |
| 1530 | return CURLE_OK; |
| 1531 | } |
Show 27 lines (not cited by the judge)
| 159 | ALLOC_FUNC void *curl_dbg_calloc(size_t wanted_elements, size_t wanted_size, |
| 160 | int line, const char *source) |
| 161 | { |
| 162 | struct memdebug *mem; |
| 163 | size_t size, user_size; |
| 164 | |
| 165 | DEBUGASSERT(wanted_elements != 0); |
| 166 | DEBUGASSERT(wanted_size != 0); |
| 167 | |
| 168 | if(countcheck("calloc", line, source)) |
| 169 | return NULL; |
| 170 | |
| 171 | /* alloc at least 64 bytes */ |
| 172 | user_size = wanted_size * wanted_elements; |
| 173 | size = sizeof(struct memdebug) + user_size; |
| 174 | |
| 175 | mem = (Curl_ccalloc)(1, size); |
| 176 | if(mem) |
| 177 | mem->size = user_size; |
| 178 | |
| 179 | if(source) |
| 180 | curl_dbg_log("MEM %s:%d calloc(%zu,%zu) = %p\n", |
| 181 | source, line, wanted_elements, wanted_size, |
| 182 | mem ? (void *)mem->mem : (void *)0); |
| 183 | |
| 184 | return (mem ? mem->mem : NULL); |
| 185 | } |
Show 161 lines (not cited by the judge)
| 1808 | static CURLMcode multi_runsingle(struct Curl_multi *multi, |
| 1809 | struct curltime *nowp, |
| 1810 | struct Curl_easy *data) |
| 1811 | { |
| 1812 | struct Curl_message *msg = NULL; |
| 1813 | bool connected; |
| 1814 | bool async; |
| 1815 | bool protocol_connected = FALSE; |
| 1816 | bool dophase_done = FALSE; |
| 1817 | bool done = FALSE; |
| 1818 | CURLMcode rc; |
| 1819 | CURLcode result = CURLE_OK; |
| 1820 | timediff_t recv_timeout_ms; |
| 1821 | timediff_t send_timeout_ms; |
| 1822 | int control; |
| 1823 | |
| 1824 | if(!GOOD_EASY_HANDLE(data)) |
| 1825 | return CURLM_BAD_EASY_HANDLE; |
| 1826 | |
| 1827 | if(multi->dead) { |
| 1828 | /* a multi-level callback returned error before, meaning every individual |
| 1829 | transfer now has failed */ |
| 1830 | result = CURLE_ABORTED_BY_CALLBACK; |
| 1831 | Curl_posttransfer(data); |
| 1832 | multi_done(data, result, FALSE); |
| 1833 | multistate(data, MSTATE_COMPLETED); |
| 1834 | } |
| 1835 | |
| 1836 | do { |
| 1837 | /* A "stream" here is a logical stream if the protocol can handle that |
| 1838 | (HTTP/2), or the full connection for older protocols */ |
| 1839 | bool stream_error = FALSE; |
| 1840 | rc = CURLM_OK; |
| 1841 | |
| 1842 | if(multi_ischanged(multi, TRUE)) { |
| 1843 | DEBUGF(infof(data, "multi changed, check CONNECT_PEND queue")); |
| 1844 | process_pending_handles(multi); /* multiplexed */ |
| 1845 | } |
| 1846 | |
| 1847 | if(data->mstate > MSTATE_CONNECT && |
| 1848 | data->mstate < MSTATE_COMPLETED) { |
| 1849 | /* Make sure we set the connection's current owner */ |
| 1850 | DEBUGASSERT(data->conn); |
| 1851 | if(!data->conn) |
| 1852 | return CURLM_INTERNAL_ERROR; |
| 1853 | } |
| 1854 | |
| 1855 | if(data->conn && |
| 1856 | (data->mstate >= MSTATE_CONNECT) && |
| 1857 | (data->mstate < MSTATE_COMPLETED)) { |
| 1858 | /* Check for overall operation timeout here but defer handling the |
| 1859 | * connection timeout to later, to allow for a connection to be set up |
| 1860 | * in the window since we last checked timeout. This prevents us |
| 1861 | * tearing down a completed connection in the case where we were slow |
| 1862 | * to check the timeout (e.g. process descheduled during this loop). |
| 1863 | * We set connect_timeout=FALSE to do this. */ |
| 1864 | |
| 1865 | /* we need to wait for the connect state as only then is the start time |
| 1866 | stored, but we must not check already completed handles */ |
| 1867 | if(multi_handle_timeout(data, nowp, &stream_error, &result, FALSE)) { |
| 1868 | /* Skip the statemachine and go directly to error handling section. */ |
| 1869 | goto statemachine_end; |
| 1870 | } |
| 1871 | } |
| 1872 | |
| 1873 | switch(data->mstate) { |
| 1874 | case MSTATE_INIT: |
| 1875 | /* init this transfer. */ |
| 1876 | result = Curl_pretransfer(data); |
| 1877 | |
| 1878 | if(!result) { |
| 1879 | /* after init, go CONNECT */ |
| 1880 | multistate(data, MSTATE_CONNECT); |
| 1881 | *nowp = Curl_pgrsTime(data, TIMER_STARTOP); |
| 1882 | rc = CURLM_CALL_MULTI_PERFORM; |
| 1883 | } |
| 1884 | break; |
| 1885 | |
| 1886 | case MSTATE_PENDING: |
| 1887 | /* We will stay here until there is a connection available. Then |
| 1888 | we try again in the MSTATE_CONNECT state. */ |
| 1889 | break; |
| 1890 | |
| 1891 | case MSTATE_CONNECT: |
| 1892 | /* Connect. We want to get a connection identifier filled in. */ |
| 1893 | /* init this transfer. */ |
| 1894 | result = Curl_preconnect(data); |
| 1895 | if(result) |
| 1896 | break; |
| 1897 | |
| 1898 | *nowp = Curl_pgrsTime(data, TIMER_STARTSINGLE); |
| 1899 | if(data->set.timeout) |
| 1900 | Curl_expire(data, data->set.timeout, EXPIRE_TIMEOUT); |
| 1901 | |
| 1902 | if(data->set.connecttimeout) |
| 1903 | Curl_expire(data, data->set.connecttimeout, EXPIRE_CONNECTTIMEOUT); |
| 1904 | |
| 1905 | result = Curl_connect(data, &async, &protocol_connected); |
| 1906 | if(CURLE_NO_CONNECTION_AVAILABLE == result) { |
| 1907 | /* There was no connection available. We will go to the pending |
| 1908 | state and wait for an available connection. */ |
| 1909 | multistate(data, MSTATE_PENDING); |
| 1910 | |
| 1911 | /* add this handle to the list of connect-pending handles */ |
| 1912 | Curl_llist_insert_next(&multi->pending, multi->pending.tail, data, |
| 1913 | &data->connect_queue); |
| 1914 | result = CURLE_OK; |
| 1915 | break; |
| 1916 | } |
| 1917 | else if(data->state.previouslypending) { |
| 1918 | /* this transfer comes from the pending queue so try move another */ |
| 1919 | infof(data, "Transfer was pending, now try another"); |
| 1920 | process_pending_handles(data->multi); |
| 1921 | } |
| 1922 | |
| 1923 | if(!result) { |
| 1924 | if(async) |
| 1925 | /* We're now waiting for an asynchronous name lookup */ |
| 1926 | multistate(data, MSTATE_RESOLVING); |
| 1927 | else { |
| 1928 | /* after the connect has been sent off, go WAITCONNECT unless the |
| 1929 | protocol connect is already done and we can go directly to |
| 1930 | WAITDO or DO! */ |
| 1931 | rc = CURLM_CALL_MULTI_PERFORM; |
| 1932 | |
| 1933 | if(protocol_connected) |
| 1934 | multistate(data, MSTATE_DO); |
| 1935 | else { |
| 1936 | #ifndef CURL_DISABLE_HTTP |
| 1937 | if(Curl_connect_ongoing(data->conn)) |
| 1938 | multistate(data, MSTATE_TUNNELING); |
| 1939 | else |
| 1940 | #endif |
| 1941 | multistate(data, MSTATE_CONNECTING); |
| 1942 | } |
| 1943 | } |
| 1944 | } |
| 1945 | break; |
| 1946 | |
| 1947 | case MSTATE_RESOLVING: |
| 1948 | /* awaiting an asynch name resolve to complete */ |
| 1949 | { |
| 1950 | struct Curl_dns_entry *dns = NULL; |
| 1951 | struct connectdata *conn = data->conn; |
| 1952 | const char *hostname; |
| 1953 | |
| 1954 | DEBUGASSERT(conn); |
| 1955 | #ifndef CURL_DISABLE_PROXY |
| 1956 | if(conn->bits.httpproxy) |
| 1957 | hostname = conn->http_proxy.host.name; |
| 1958 | else |
| 1959 | #endif |
| 1960 | if(conn->bits.conn_to_host) |
| 1961 | hostname = conn->conn_to_host.name; |
| 1962 | else |
| 1963 | hostname = conn->host.name; |
| 1964 | |
| 1965 | /* check if we have the name resolved by now */ |
| 1966 | dns = Curl_fetch_addr(data, hostname, (int)conn->port); |
| 1967 | |
| 1968 | if(dns) { |
Show 161 lines (not cited by the judge)
| 356 | static CURLcode post_per_transfer(struct GlobalConfig *global, |
| 357 | struct per_transfer *per, |
| 358 | CURLcode result, |
| 359 | bool *retryp, |
| 360 | long *delay) /* milliseconds! */ |
| 361 | { |
| 362 | struct OutStruct *outs = &per->outs; |
| 363 | CURL *curl = per->curl; |
| 364 | struct OperationConfig *config = per->config; |
| 365 | |
| 366 | if(!curl || !config) |
| 367 | return result; |
| 368 | |
| 369 | *retryp = FALSE; |
| 370 | *delay = 0; /* for no retry, keep it zero */ |
| 371 | |
| 372 | if(per->infdopen) |
| 373 | close(per->infd); |
| 374 | |
| 375 | #ifdef __VMS |
| 376 | if(is_vms_shell()) { |
| 377 | /* VMS DCL shell behavior */ |
| 378 | if(!global->showerror) |
| 379 | vms_show = VMSSTS_HIDE; |
| 380 | } |
| 381 | else |
| 382 | #endif |
| 383 | if(!config->synthetic_error && result && global->showerror) { |
| 384 | const char *msg = per->errorbuffer; |
| 385 | fprintf(global->errors, "curl: (%d) %s\n", result, |
| 386 | (msg && msg[0]) ? msg : curl_easy_strerror(result)); |
| 387 | if(result == CURLE_PEER_FAILED_VERIFICATION) |
| 388 | fputs(CURL_CA_CERT_ERRORMSG, global->errors); |
| 389 | } |
| 390 | else if(config->failwithbody) { |
| 391 | /* if HTTP response >= 400, return error */ |
| 392 | long code = 0; |
| 393 | curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); |
| 394 | if(code >= 400) { |
| 395 | if(global->showerror) |
| 396 | fprintf(global->errors, |
| 397 | "curl: (%d) The requested URL returned error: %ld\n", |
| 398 | CURLE_HTTP_RETURNED_ERROR, code); |
| 399 | result = CURLE_HTTP_RETURNED_ERROR; |
| 400 | } |
| 401 | } |
| 402 | /* Set file extended attributes */ |
| 403 | if(!result && config->xattr && outs->fopened && outs->stream) { |
| 404 | int rc = fwrite_xattr(curl, fileno(outs->stream)); |
| 405 | if(rc) |
| 406 | warnf(config->global, "Error setting extended attributes on '%s': %s\n", |
| 407 | outs->filename, strerror(errno)); |
| 408 | } |
| 409 | |
| 410 | if(!result && !outs->stream && !outs->bytes) { |
| 411 | /* we have received no data despite the transfer was successful |
| 412 | ==> force creation of an empty output file (if an output file |
| 413 | was specified) */ |
| 414 | long cond_unmet = 0L; |
| 415 | /* do not create (or even overwrite) the file in case we get no |
| 416 | data because of unmet condition */ |
| 417 | curl_easy_getinfo(curl, CURLINFO_CONDITION_UNMET, &cond_unmet); |
| 418 | if(!cond_unmet && !tool_create_output_file(outs, config)) |
| 419 | result = CURLE_WRITE_ERROR; |
| 420 | } |
| 421 | |
| 422 | if(!outs->s_isreg && outs->stream) { |
| 423 | /* Dump standard stream buffered data */ |
| 424 | int rc = fflush(outs->stream); |
| 425 | if(!result && rc) { |
| 426 | /* something went wrong in the writing process */ |
| 427 | result = CURLE_WRITE_ERROR; |
| 428 | if(global->showerror) |
| 429 | fprintf(global->errors, "curl: (%d) Failed writing body\n", result); |
| 430 | } |
| 431 | } |
| 432 | |
| 433 | /* if retry-max-time is non-zero, make sure we haven't exceeded the |
| 434 | time */ |
| 435 | if(per->retry_numretries && |
| 436 | (!config->retry_maxtime || |
| 437 | (tvdiff(tvnow(), per->retrystart) < |
| 438 | config->retry_maxtime*1000L)) ) { |
| 439 | enum { |
| 440 | RETRY_NO, |
| 441 | RETRY_ALL_ERRORS, |
| 442 | RETRY_TIMEOUT, |
| 443 | RETRY_CONNREFUSED, |
| 444 | RETRY_HTTP, |
| 445 | RETRY_FTP, |
| 446 | RETRY_LAST /* not used */ |
| 447 | } retry = RETRY_NO; |
| 448 | long response = 0; |
| 449 | if((CURLE_OPERATION_TIMEDOUT == result) || |
| 450 | (CURLE_COULDNT_RESOLVE_HOST == result) || |
| 451 | (CURLE_COULDNT_RESOLVE_PROXY == result) || |
| 452 | (CURLE_FTP_ACCEPT_TIMEOUT == result)) |
| 453 | /* retry timeout always */ |
| 454 | retry = RETRY_TIMEOUT; |
| 455 | else if(config->retry_connrefused && |
| 456 | (CURLE_COULDNT_CONNECT == result)) { |
| 457 | long oserrno = 0; |
| 458 | curl_easy_getinfo(curl, CURLINFO_OS_ERRNO, &oserrno); |
| 459 | if(ECONNREFUSED == oserrno) |
| 460 | retry = RETRY_CONNREFUSED; |
| 461 | } |
| 462 | else if((CURLE_OK == result) || |
| 463 | ((config->failonerror || config->failwithbody) && |
| 464 | (CURLE_HTTP_RETURNED_ERROR == result))) { |
| 465 | /* If it returned OK. _or_ failonerror was enabled and it |
| 466 | returned due to such an error, check for HTTP transient |
| 467 | errors to retry on. */ |
| 468 | const char *scheme; |
| 469 | curl_easy_getinfo(curl, CURLINFO_SCHEME, &scheme); |
| 470 | scheme = proto_token(scheme); |
| 471 | if(scheme == proto_http || scheme == proto_https) { |
| 472 | /* This was HTTP(S) */ |
| 473 | curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response); |
| 474 | |
| 475 | switch(response) { |
| 476 | case 408: /* Request Timeout */ |
| 477 | case 429: /* Too Many Requests (RFC6585) */ |
| 478 | case 500: /* Internal Server Error */ |
| 479 | case 502: /* Bad Gateway */ |
| 480 | case 503: /* Service Unavailable */ |
| 481 | case 504: /* Gateway Timeout */ |
| 482 | retry = RETRY_HTTP; |
| 483 | /* |
| 484 | * At this point, we have already written data to the output |
| 485 | * file (or terminal). If we write to a file, we must rewind |
| 486 | * or close/re-open the file so that the next attempt starts |
| 487 | * over from the beginning. |
| 488 | * |
| 489 | * TODO: similar action for the upload case. We might need |
| 490 | * to start over reading from a previous point if we have |
| 491 | * uploaded something when this was returned. |
| 492 | */ |
| 493 | break; |
| 494 | } |
| 495 | } |
| 496 | } /* if CURLE_OK */ |
| 497 | else if(result) { |
| 498 | const char *scheme; |
| 499 | |
| 500 | curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response); |
| 501 | curl_easy_getinfo(curl, CURLINFO_SCHEME, &scheme); |
| 502 | scheme = proto_token(scheme); |
| 503 | |
| 504 | if((scheme == proto_ftp || scheme == proto_ftps) && response / 100 == 4) |
| 505 | /* |
| 506 | * This is typically when the FTP server only allows a certain |
| 507 | * amount of users and we are not one of them. All 4xx codes |
| 508 | * are transient. |
| 509 | */ |
| 510 | retry = RETRY_FTP; |
| 511 | } |
| 512 | |
| 513 | if(result && !retry && config->retry_all_errors) |
| 514 | retry = RETRY_ALL_ERRORS; |
| 515 | |
| 516 | if(retry) { |
Show 161 lines (not cited by the judge)
| 349 | static CURLcode post_per_transfer(struct GlobalConfig *global, |
| 350 | struct per_transfer *per, |
| 351 | CURLcode result, |
| 352 | bool *retryp, |
| 353 | long *delay) /* milliseconds! */ |
| 354 | { |
| 355 | struct OutStruct *outs = &per->outs; |
| 356 | CURL *curl = per->curl; |
| 357 | struct OperationConfig *config = per->config; |
| 358 | |
| 359 | if(!curl || !config) |
| 360 | return result; |
| 361 | |
| 362 | *retryp = FALSE; |
| 363 | *delay = 0; /* for no retry, keep it zero */ |
| 364 | |
| 365 | if(per->infdopen) |
| 366 | close(per->infd); |
| 367 | |
| 368 | #ifdef __VMS |
| 369 | if(is_vms_shell()) { |
| 370 | /* VMS DCL shell behavior */ |
| 371 | if(!global->showerror) |
| 372 | vms_show = VMSSTS_HIDE; |
| 373 | } |
| 374 | else |
| 375 | #endif |
| 376 | if(!config->synthetic_error && result && global->showerror) { |
| 377 | fprintf(global->errors, "curl: (%d) %s\n", result, |
| 378 | (per->errorbuffer[0]) ? per->errorbuffer : |
| 379 | curl_easy_strerror(result)); |
| 380 | if(result == CURLE_PEER_FAILED_VERIFICATION) |
| 381 | fputs(CURL_CA_CERT_ERRORMSG, global->errors); |
| 382 | } |
| 383 | else if(config->failwithbody) { |
| 384 | /* if HTTP response >= 400, return error */ |
| 385 | long code = 0; |
| 386 | curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &code); |
| 387 | if(code >= 400) { |
| 388 | if(global->showerror) |
| 389 | fprintf(global->errors, |
| 390 | "curl: (%d) The requested URL returned error: %ld\n", |
| 391 | CURLE_HTTP_RETURNED_ERROR, code); |
| 392 | result = CURLE_HTTP_RETURNED_ERROR; |
| 393 | } |
| 394 | } |
| 395 | /* Set file extended attributes */ |
| 396 | if(!result && config->xattr && outs->fopened && outs->stream) { |
| 397 | int rc = fwrite_xattr(curl, fileno(outs->stream)); |
| 398 | if(rc) |
| 399 | warnf(config->global, "Error setting extended attributes on '%s': %s\n", |
| 400 | outs->filename, strerror(errno)); |
| 401 | } |
| 402 | |
| 403 | if(!result && !outs->stream && !outs->bytes) { |
| 404 | /* we have received no data despite the transfer was successful |
| 405 | ==> force creation of an empty output file (if an output file |
| 406 | was specified) */ |
| 407 | long cond_unmet = 0L; |
| 408 | /* do not create (or even overwrite) the file in case we get no |
| 409 | data because of unmet condition */ |
| 410 | curl_easy_getinfo(curl, CURLINFO_CONDITION_UNMET, &cond_unmet); |
| 411 | if(!cond_unmet && !tool_create_output_file(outs, config)) |
| 412 | result = CURLE_WRITE_ERROR; |
| 413 | } |
| 414 | |
| 415 | if(!outs->s_isreg && outs->stream) { |
| 416 | /* Dump standard stream buffered data */ |
| 417 | int rc = fflush(outs->stream); |
| 418 | if(!result && rc) { |
| 419 | /* something went wrong in the writing process */ |
| 420 | result = CURLE_WRITE_ERROR; |
| 421 | if(global->showerror) |
| 422 | fprintf(global->errors, "curl: (%d) Failed writing body\n", result); |
| 423 | } |
| 424 | } |
| 425 | |
| 426 | /* if retry-max-time is non-zero, make sure we haven't exceeded the |
| 427 | time */ |
| 428 | if(per->retry_numretries && |
| 429 | (!config->retry_maxtime || |
| 430 | (tvdiff(tvnow(), per->retrystart) < |
| 431 | config->retry_maxtime*1000L)) ) { |
| 432 | enum { |
| 433 | RETRY_NO, |
| 434 | RETRY_ALL_ERRORS, |
| 435 | RETRY_TIMEOUT, |
| 436 | RETRY_CONNREFUSED, |
| 437 | RETRY_HTTP, |
| 438 | RETRY_FTP, |
| 439 | RETRY_LAST /* not used */ |
| 440 | } retry = RETRY_NO; |
| 441 | long response = 0; |
| 442 | if((CURLE_OPERATION_TIMEDOUT == result) || |
| 443 | (CURLE_COULDNT_RESOLVE_HOST == result) || |
| 444 | (CURLE_COULDNT_RESOLVE_PROXY == result) || |
| 445 | (CURLE_FTP_ACCEPT_TIMEOUT == result)) |
| 446 | /* retry timeout always */ |
| 447 | retry = RETRY_TIMEOUT; |
| 448 | else if(config->retry_connrefused && |
| 449 | (CURLE_COULDNT_CONNECT == result)) { |
| 450 | long oserrno = 0; |
| 451 | curl_easy_getinfo(curl, CURLINFO_OS_ERRNO, &oserrno); |
| 452 | if(ECONNREFUSED == oserrno) |
| 453 | retry = RETRY_CONNREFUSED; |
| 454 | } |
| 455 | else if((CURLE_OK == result) || |
| 456 | ((config->failonerror || config->failwithbody) && |
| 457 | (CURLE_HTTP_RETURNED_ERROR == result))) { |
| 458 | /* If it returned OK. _or_ failonerror was enabled and it |
| 459 | returned due to such an error, check for HTTP transient |
| 460 | errors to retry on. */ |
| 461 | long protocol = 0; |
| 462 | curl_easy_getinfo(curl, CURLINFO_PROTOCOL, &protocol); |
| 463 | if((protocol == CURLPROTO_HTTP) || (protocol == CURLPROTO_HTTPS)) { |
| 464 | /* This was HTTP(S) */ |
| 465 | curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response); |
| 466 | |
| 467 | switch(response) { |
| 468 | case 408: /* Request Timeout */ |
| 469 | case 429: /* Too Many Requests (RFC6585) */ |
| 470 | case 500: /* Internal Server Error */ |
| 471 | case 502: /* Bad Gateway */ |
| 472 | case 503: /* Service Unavailable */ |
| 473 | case 504: /* Gateway Timeout */ |
| 474 | retry = RETRY_HTTP; |
| 475 | /* |
| 476 | * At this point, we have already written data to the output |
| 477 | * file (or terminal). If we write to a file, we must rewind |
| 478 | * or close/re-open the file so that the next attempt starts |
| 479 | * over from the beginning. |
| 480 | * |
| 481 | * TODO: similar action for the upload case. We might need |
| 482 | * to start over reading from a previous point if we have |
| 483 | * uploaded something when this was returned. |
| 484 | */ |
| 485 | break; |
| 486 | } |
| 487 | } |
| 488 | } /* if CURLE_OK */ |
| 489 | else if(result) { |
| 490 | long protocol = 0; |
| 491 | |
| 492 | curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response); |
| 493 | curl_easy_getinfo(curl, CURLINFO_PROTOCOL, &protocol); |
| 494 | |
| 495 | if((protocol == CURLPROTO_FTP || protocol == CURLPROTO_FTPS) && |
| 496 | response / 100 == 4) |
| 497 | /* |
| 498 | * This is typically when the FTP server only allows a certain |
| 499 | * amount of users and we are not one of them. All 4xx codes |
| 500 | * are transient. |
| 501 | */ |
| 502 | retry = RETRY_FTP; |
| 503 | } |
| 504 | |
| 505 | if(result && !retry && config->retry_all_errors) |
| 506 | retry = RETRY_ALL_ERRORS; |
| 507 | |
| 508 | if(retry) { |
| 509 | long sleeptime = 0; |
Show 81 lines (not cited by the judge)
| 2372 | static CURLcode serial_transfers(struct GlobalConfig *global, |
| 2373 | CURLSH *share) |
| 2374 | { |
| 2375 | CURLcode returncode = CURLE_OK; |
| 2376 | CURLcode result = CURLE_OK; |
| 2377 | struct per_transfer *per; |
| 2378 | bool added = FALSE; |
| 2379 | |
| 2380 | result = create_transfer(global, share, &added); |
| 2381 | if(result) |
| 2382 | return result; |
| 2383 | if(!added) { |
| 2384 | errorf(global, "no transfer performed\n"); |
| 2385 | return CURLE_READ_ERROR; |
| 2386 | } |
| 2387 | for(per = transfers; per;) { |
| 2388 | bool retry; |
| 2389 | long delay_ms; |
| 2390 | bool bailout = FALSE; |
| 2391 | struct timeval start; |
| 2392 | result = pre_transfer(global, per); |
| 2393 | if(result) |
| 2394 | break; |
| 2395 | |
| 2396 | #ifndef CURL_DISABLE_LIBCURL_OPTION |
| 2397 | if(global->libcurl) { |
| 2398 | result = easysrc_perform(); |
| 2399 | if(result) |
| 2400 | break; |
| 2401 | } |
| 2402 | #endif |
| 2403 | start = tvnow(); |
| 2404 | #ifdef CURLDEBUG |
| 2405 | if(global->test_event_based) |
| 2406 | result = curl_easy_perform_ev(per->curl); |
| 2407 | else |
| 2408 | #endif |
| 2409 | result = curl_easy_perform(per->curl); |
| 2410 | |
| 2411 | returncode = post_per_transfer(global, per, result, &retry, &delay_ms); |
| 2412 | if(retry) { |
| 2413 | tool_go_sleep(delay_ms); |
| 2414 | continue; |
| 2415 | } |
| 2416 | |
| 2417 | /* Bail out upon critical errors or --fail-early */ |
| 2418 | if(is_fatal_error(returncode) || (returncode && global->fail_early)) |
| 2419 | bailout = TRUE; |
| 2420 | else { |
| 2421 | /* setup the next one just before we delete this */ |
| 2422 | result = create_transfer(global, share, &added); |
| 2423 | if(result) |
| 2424 | bailout = TRUE; |
| 2425 | } |
| 2426 | |
| 2427 | per = del_per_transfer(per); |
| 2428 | |
| 2429 | if(bailout) |
| 2430 | break; |
| 2431 | |
| 2432 | if(per && global->ms_per_transfer) { |
| 2433 | /* how long time did the most recent transfer take in number of |
| 2434 | milliseconds */ |
| 2435 | long milli = tvdiff(tvnow(), start); |
| 2436 | if(milli < global->ms_per_transfer) { |
| 2437 | notef(global, "Transfer took %ld ms, waits %ldms as set by --rate\n", |
| 2438 | milli, global->ms_per_transfer - milli); |
| 2439 | /* The transfer took less time than wanted. Wait a little. */ |
| 2440 | tool_go_sleep(global->ms_per_transfer - milli); |
| 2441 | } |
| 2442 | } |
| 2443 | } |
| 2444 | if(returncode) |
| 2445 | /* returncode errors have priority */ |
| 2446 | result = returncode; |
| 2447 | |
| 2448 | if(result) |
| 2449 | single_transfer_cleanup(global->current); |
| 2450 | |
| 2451 | return result; |
| 2452 | } |