✓ProveIt
Examples / Report #1722065 Saved analysis

CVE-2022-42915: HTTP proxy double-free

curl · 2022-10-0417.1s analysis · $0.041 in model calls

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

Code supports the claim

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

Why this finding?
  • The code 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.

1 · Grounding
partially grounded
no model · 38 references checked out, 4 did not
2 · Claims
NVIDIA-Nemotron-3-Nano-30B-A3B
5 claims · 2610 tokens · $0.0002
3 · Evidence
26 code snippets
real curl source at a2d0154e93ee
4 · Judgment
Nemotron-3-Ultra-550b-a55b
34600 tokens · 15.2s · $0.0411
5 · Receipts
10 of 12 citations verified
every citation must point at code the judge was shown
Review a draft reply to the reporter

Edit and verify before sending.

Thank you for 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

C1flaw supported

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).

C2location supported

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).

C3trigger supported

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.

C4impact supported

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.

C5reproduction supported

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)

Full report on HackerOne ↗

This is a finding that Trail of Bits found in their ongoing curl security audit. Reported at a status meeting today. ## Summary: curl frees memory twice in some cleanup function related to HTTP proxies. It as simple as `curl -x http://localhost:80 dict://127.0.0.1` Using valgrind on the current git master, it shows: ==55921== Memcheck, a memory error detector ==55921== Copyright (C) 2002-2022, and GNU GPL'd, by Julian Seward et al. ==55921== Using Valgrind-3.19.0 and LibVEX; rerun with -h for copyright info ==55921== Command: ./src/curl -x http://localhost:80 dict://127.0.0.1 ==55921== Parent PID: 3035 ==55921== ==55921== Invalid free() / delete / delete[] / realloc() ==55921== at 0x484617B: free (vg_replace_malloc.c:872) ==55921== by 0x152464: curl_dbg_free (memdebug.c:297) ==55921== by 0x17E11C: Curl_free_request_state (url.c:2259) ==55921== by 0x179B38: Curl_close (url.c:421) ==55921== by 0x1482DD: curl_easy_cleanup (easy.c:799) ==55921== by 0x1359F4: post_per_transfer (tool_operate.c:657) ==55921== by 0x13D085: serial_transfers (tool_operate.c:2431) ==55921== by 0x13D5FC: run_all_transfers (tool_operate.c:2617) ==55921== by 0x13D972: operate (tool_operate.c:2729) ==55921== by 0x13427C: main (tool_main.c:276) ==55921== Address 0x5b1c790 is 0 bytes inside a block of size 984 free'd ==55921== at 0x484617B: free (vg_replace_malloc.c:872) ==55921== by 0x152464: curl_dbg_free (memdebug.c:297) ==55921== by 0x17AE5E: conn_free (url.c:810) ==55921== by 0x17B132: Curl_disconnect (url.c:893) ==55921== by 0x15D523: multi_runsingle (multi.c:2614) ==55921== by 0x15D7B6: curl_multi_perform (multi.c:2683) ==55921== by 0x147FFB: easy_transfer (easy.c:663) ==55921== by 0x14822C: easy_perform (easy.c:753) ==55921== by 0x148276: curl_easy_perform (easy.c:772) ==55921== by 0x13D064: serial_transfers (tool_operate.c:2429) ==55921== by 0x13D5FC: run_all_transfers (tool_operate.c:2617) ==55921== by 0x13D972: operate (tool_operate.c:2729) ==55921== Block was alloc'd at ==55921== at 0x48485EF: calloc (vg_replace_malloc.c:1328) ==55921== by 0x1521A6: curl_dbg_calloc (memdebug.c:175) ==55921== by 0x1BEC8F: connect_init (http_proxy.c:174) ==55921== by 0x1C02C2: Curl_proxyCONNECT (http_proxy.c:1061) ==55921== by 0x1BEA43: Curl_proxy_connect (http_proxy.c:118) ==55921== by 0x1B67D4: Curl_http_c …

Source code

lib/memdebug.clines 272–322 · lines 297-297 cited by the report
272 mem->size = wantedsize;
273 return mem->mem;
274 }
275
276 return NULL;
277}
278
279void 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
304curl_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
lib/url.clines 2234–2284 · lines 2259-2259 cited by the report
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
2257void 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****************************************************************/
2276static 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;
lib/url.clines 783–825 · definition of conn_free
783static 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}
lib/http_proxy.clines 158–204 · definition of connect_init
158static 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}
lib/curl_memory.hlines 132–132 · definition of free
Show 1 lines (not cited by the judge)
132#define free(ptr) Curl_cfree(ptr)
lib/url.clines 379–497 · lines 421-421 cited by the report; definition of Curl_close
Show 119 lines (not cited by the judge)
379CURLcode 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}
lib/easy.clines 705–824 · lines 799-799 cited by the report; lines 753-753 cited by the report; definition of easy_perform
Show 120 lines (not cited by the judge)
705static 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 */
770CURLcode 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 */
780CURLcode 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 */
791void 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
808CURLcode 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
823static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src)
824{
src/tool_operate.clines 632–682 · lines 657-657 cited by the report
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
669static 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 */
src/tool_operate.clines 2393–2472 · lines 2431-2431 cited by the report; lines 2429-2429 cited by the report; definition of serial_transfers
Show 80 lines (not cited by the judge)
2393static 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}
src/tool_operate.clines 2592–2642 · lines 2617-2617 cited by the report
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
2603static 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
src/tool_operate.clines 2704–2746 · lines 2729-2729 cited by the report
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}
lib/url.clines 841–918 · lines 893-893 cited by the report; definition of Curl_disconnect
Show 78 lines (not cited by the judge)
841void 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 */
901static 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.
lib/multi.clines 2589–2639 · lines 2614-2614 cited by the report
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) {
lib/multi.clines 2658–2716 · lines 2683-2683 cited by the report; definition of curl_multi_perform
Show 59 lines (not cited by the judge)
2658
2659 data->result = result;
2660 return rc;
2661}
2662
2663
2664CURLMcode 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}
lib/easy.clines 638–688 · lines 663-663 cited by the report
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
651static 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/*
lib/http_proxy.clines 1036–1079 · lines 1061-1061 cited by the report
Show 44 lines (not cited by the judge)
1036
1037void 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
1053CURLcode 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
1074void Curl_connect_free(struct Curl_easy *data)
1075{
1076 (void)data;
1077}
1078
1079#endif /* CURL_DISABLE_PROXY */
lib/http_proxy.clines 80–143 · lines 118-118 cited by the report; definition of Curl_proxy_connect
Show 64 lines (not cited by the judge)
80CURLcode 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
130bool Curl_connect_complete(struct connectdata *conn)
131{
132 return !conn->connect_state ||
133 (conn->connect_state->tunnel_state >= TUNNEL_COMPLETE);
134}
135
136bool 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
lib/http.clines 1526–1584 · lines 1551-1551 cited by the report; definition of Curl_http_connect
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 */
1540CURLcode 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}
lib/multi.clines 2002–2052 · lines 2027-2027 cited by the report
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;
lib/url.c@curl-7_85_0lines 837–890 · definition of Curl_disconnect as in release curl-7_85_0; differs from master; commits that changed it since: 664249d095 ws: initial websockets support
Show 54 lines (not cited by the judge)
837void 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}
lib/http.c@curl-7_85_0lines 1487–1531 · definition of Curl_http_connect as in release curl-7_85_0; differs from master; commits that changed it since: 664249d095 ws: initial websockets support
Show 45 lines (not cited by the judge)
1487CURLcode 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}
lib/memdebug.clines 159–185 · definition of curl_dbg_calloc
Show 27 lines (not cited by the judge)
159ALLOC_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}
lib/multi.clines 1808–1968 · definition of multi_runsingle (truncated)
Show 161 lines (not cited by the judge)
1808static 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) {
src/tool_operate.clines 356–516 · definition of post_per_transfer (truncated)
Show 161 lines (not cited by the judge)
356static 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) {
src/tool_operate.c@curl-7_85_0lines 349–509 · definition of post_per_transfer as in release curl-7_85_0; differs from master; commits that changed it since: 677266c769 tool: remove protocol count limitation | dd2a024323 cli tool: do not use disabled protocols | 7be53774c4 tool_operate: reduce errorbuffer allocs
Show 161 lines (not cited by the judge)
349static 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;
src/tool_operate.c@curl-7_85_0lines 2372–2452 · definition of serial_transfers as in release curl-7_85_0; differs from master; commits that changed it since: ee56e6ff12 tool_operate: avoid a few #ifdefs for disabled-libcurl builds
Show 81 lines (not cited by the judge)
2372static 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}