/*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | * / __| | | | |_) | | * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at https://curl.se/docs/copyright.html. * * You may opt to use, copy, modify, merge, publish, distribute and/or sell * copies of the Software, and permit persons to whom the Software is * furnished to do so, under the terms of the COPYING file. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ***************************************************************************/ #include "curl_setup.h" /* * See comment in curl_memory.h for the explanation of this sanity check. */ #ifdef CURLX_NO_MEMORY_CALLBACKS #error "libcurl shall not ever be built with CURLX_NO_MEMORY_CALLBACKS defined" #endif #ifdef HAVE_NETINET_IN_H #include #endif #ifdef HAVE_NETDB_H #include #endif #ifdef HAVE_ARPA_INET_H #include #endif #ifdef HAVE_NET_IF_H #include #endif #ifdef HAVE_SYS_IOCTL_H #include #endif #ifdef HAVE_SYS_PARAM_H #include #endif #include "urldata.h" #include #include "transfer.h" #include "vtls/vtls.h" #include "url.h" #include "getinfo.h" #include "hostip.h" #include "share.h" #include "strdup.h" #include "progress.h" #include "easyif.h" #include "multiif.h" #include "select.h" #include "sendf.h" /* for failf function prototype */ #include "connect.h" /* for Curl_getconnectinfo */ #include "slist.h" #include "mime.h" #include "amigaos.h" #include "non-ascii.h" #include "warnless.h" #include "multiif.h" #include "sigpipe.h" #include "vssh/ssh.h" #include "setopt.h" #include "http_digest.h" #include "system_win32.h" #include "http2.h" #include "dynbuf.h" #include "altsvc.h" #include "hsts.h" /* The last 3 #include files should be in this order */ #include "curl_printf.h" #include "curl_memory.h" #include "memdebug.h" /* true globals -- for curl_global_init() and curl_global_cleanup() */ static unsigned int initialized; static long init_flags; /* * strdup (and other memory functions) is redefined in complicated * ways, but at this point it must be defined as the system-supplied strdup * so the callback pointer is initialized correctly. */ #if defined(_WIN32_WCE) #define system_strdup _strdup #elif !defined(HAVE_STRDUP) #define system_strdup curlx_strdup #else #define system_strdup strdup #endif #if defined(_MSC_VER) && defined(_DLL) && !defined(__POCC__) # pragma warning(disable:4232) /* MSVC extension, dllimport identity */ #endif /* * If a memory-using function (like curl_getenv) is used before * curl_global_init() is called, we need to have these pointers set already. */ curl_malloc_callback Curl_cmalloc = (curl_malloc_callback)malloc; curl_free_callback Curl_cfree = (curl_free_callback)free; curl_realloc_callback Curl_crealloc = (curl_realloc_callback)realloc; curl_strdup_callback Curl_cstrdup = (curl_strdup_callback)system_strdup; curl_calloc_callback Curl_ccalloc = (curl_calloc_callback)calloc; #if defined(WIN32) && defined(UNICODE) curl_wcsdup_callback Curl_cwcsdup = (curl_wcsdup_callback)_wcsdup; #endif #if defined(_MSC_VER) && defined(_DLL) && !defined(__POCC__) # pragma warning(default:4232) /* MSVC extension, dllimport identity */ #endif #ifdef DEBUGBUILD static char *leakpointer; #endif /** * curl_global_init() globally initializes curl given a bitwise set of the * different features of what to initialize. */ static CURLcode global_init(long flags, bool memoryfuncs) { if(initialized++) return CURLE_OK; if(memoryfuncs) { /* Setup the default memory functions here (again) */ Curl_cmalloc = (curl_malloc_callback)malloc; Curl_cfree = (curl_free_callback)free; Curl_crealloc = (curl_realloc_callback)realloc; Curl_cstrdup = (curl_strdup_callback)system_strdup; Curl_ccalloc = (curl_calloc_callback)calloc; #if defined(WIN32) && defined(UNICODE) Curl_cwcsdup = (curl_wcsdup_callback)_wcsdup; #endif } if(!Curl_ssl_init()) { DEBUGF(fprintf(stderr, "Error: Curl_ssl_init failed\n")); goto fail; } #ifdef WIN32 if(Curl_win32_init(flags)) { DEBUGF(fprintf(stderr, "Error: win32_init failed\n")); goto fail; } #endif #ifdef __AMIGA__ if(!Curl_amiga_init()) { DEBUGF(fprintf(stderr, "Error: Curl_amiga_init failed\n")); goto fail; } #endif #ifdef NETWARE if(netware_init()) { DEBUGF(fprintf(stderr, "Warning: LONG namespace not available\n")); } #endif if(Curl_resolver_global_init()) { DEBUGF(fprintf(stderr, "Error: resolver_global_init failed\n")); goto fail; } #if defined(USE_SSH) if(Curl_ssh_init()) { goto fail; } #endif #ifdef USE_WOLFSSH if(WS_SUCCESS != wolfSSH_Init()) { DEBUGF(fprintf(stderr, "Error: wolfSSH_Init failed\n")); return CURLE_FAILED_INIT; } #endif init_flags = flags; #ifdef DEBUGBUILD if(getenv("CURL_GLOBAL_INIT")) /* alloc data that will leak if *cleanup() is not called! */ leakpointer = malloc(1); #endif return CURLE_OK; fail: initialized--; /* undo the increase */ return CURLE_FAILED_INIT; } /** * curl_global_init() globally initializes curl given a bitwise set of the * different features of what to initialize. */ CURLcode curl_global_init(long flags) { return global_init(flags, TRUE); } /* * curl_global_init_mem() globally initializes curl and also registers the * user provided callback routines. */ CURLcode curl_global_init_mem(long flags, curl_malloc_callback m, curl_free_callback f, curl_realloc_callback r, curl_strdup_callback s, curl_calloc_callback c) { /* Invalid input, return immediately */ if(!m || !f || !r || !s || !c) return CURLE_FAILED_INIT; if(initialized) { /* Already initialized, don't do it again, but bump the variable anyway to work like curl_global_init() and require the same amount of cleanup calls. */ initialized++; return CURLE_OK; } /* set memory functions before global_init() in case it wants memory functions */ Curl_cmalloc = m; Curl_cfree = f; Curl_cstrdup = s; Curl_crealloc = r; Curl_ccalloc = c; /* Call the actual init function, but without setting */ return global_init(flags, FALSE); } /** * curl_global_cleanup() globally cleanups curl, uses the value of * "init_flags" to determine what needs to be cleaned up and what doesn't. */ void curl_global_cleanup(void) { if(!initialized) return; if(--initialized) return; Curl_ssl_cleanup(); Curl_resolver_global_cleanup(); #ifdef WIN32 Curl_win32_cleanup(init_flags); #endif Curl_amiga_cleanup(); Curl_ssh_cleanup(); #ifdef USE_WOLFSSH (void)wolfSSH_Cleanup(); #endif #ifdef DEBUGBUILD free(leakpointer); #endif init_flags = 0; } /* * curl_easy_init() is the external interface to alloc, setup and init an * easy handle that is returned. If anything goes wrong, NULL is returned. */ struct Curl_easy *curl_easy_init(void) { CURLcode result; struct Curl_easy *data; /* Make sure we inited the global SSL stuff */ if(!initialized) { result = curl_global_init(CURL_GLOBAL_DEFAULT); if(result) { /* something in the global init failed, return nothing */ DEBUGF(fprintf(stderr, "Error: curl_global_init failed\n")); return NULL; } } /* We use curl_open() with undefined URL so far */ result = Curl_open(&data); if(result) { DEBUGF(fprintf(stderr, "Error: Curl_open failed\n")); return NULL; } return data; } #ifdef CURLDEBUG struct socketmonitor { struct socketmonitor *next; /* the next node in the list or NULL */ struct pollfd socket; /* socket info of what to monitor */ }; struct events { long ms; /* timeout, run the timeout function when reached */ bool msbump; /* set TRUE when timeout is set by callback */ int num_sockets; /* number of nodes in the monitor list */ struct socketmonitor *list; /* list of sockets to monitor */ int running_handles; /* store the returned number */ }; /* events_timer * * Callback that gets called with a new value when the timeout should be * updated. */ static int events_timer(struct Curl_multi *multi, /* multi handle */ long timeout_ms, /* see above */ void *userp) /* private callback pointer */ { struct events *ev = userp; (void)multi; if(timeout_ms == -1) /* timeout removed */ timeout_ms = 0; else if(timeout_ms == 0) /* timeout is already reached! */ timeout_ms = 1; /* trigger asap */ ev->ms = timeout_ms; ev->msbump = TRUE; return 0; } /* poll2cselect * * convert from poll() bit definitions to libcurl's CURL_CSELECT_* ones */ static int poll2cselect(int pollmask) { int omask = 0; if(pollmask & POLLIN) omask |= CURL_CSELECT_IN; if(pollmask & POLLOUT) omask |= CURL_CSELECT_OUT; if(pollmask & POLLERR) omask |= CURL_CSELECT_ERR; return omask; } /* socketcb2poll * * convert from libcurl' CURL_POLL_* bit definitions to poll()'s */ static short socketcb2poll(int pollmask) { short omask = 0; if(pollmask & CURL_POLL_IN) omask |= POLLIN; if(pollmask & CURL_POLL_OUT) omask |= POLLOUT; return omask; } /* events_socket * * Callback that gets called with information about socket activity to * monitor. */ static int events_socket(struct Curl_easy *easy, /* easy handle */ curl_socket_t s, /* socket */ int what, /* see above */ void *userp, /* private callback pointer */ void *socketp) /* private socket pointer */ { struct events *ev = userp; struct socketmonitor *m; struct socketmonitor *prev = NULL; #if defined(CURL_DISABLE_VERBOSE_STRINGS) (void) easy; #endif (void)socketp; m = ev->list; while(m) { if(m->socket.fd == s) { if(what == CURL_POLL_REMOVE) { struct socketmonitor *nxt = m->next; /* remove this node from the list of monitored sockets */ if(prev) prev->next = nxt; else ev->list = nxt; free(m); m = nxt; infof(easy, "socket cb: socket %d REMOVED", s); } else { /* The socket 's' is already being monitored, update the activity mask. Convert from libcurl bitmask to the poll one. */ m->socket.events = socketcb2poll(what); infof(easy, "socket cb: socket %d UPDATED as %s%s", s, (what&CURL_POLL_IN)?"IN":"", (what&CURL_POLL_OUT)?"OUT":""); } break; } prev = m; m = m->next; /* move to next node */ } if(!m) { if(what == CURL_POLL_REMOVE) { /* this happens a bit too often, libcurl fix perhaps? */ /* fprintf(stderr, "%s: socket %d asked to be REMOVED but not present!\n", __func__, s); */ } else { m = malloc(sizeof(struct socketmonitor)); if(m) { m->next = ev->list; m->socket.fd = s; m->socket.events = socketcb2poll(what); m->socket.revents = 0; ev->list = m; infof(easy, "socket cb: socket %d ADDED as %s%s", s, (what&CURL_POLL_IN)?"IN":"", (what&CURL_POLL_OUT)?"OUT":""); } else return CURLE_OUT_OF_MEMORY; } } return 0; } /* * events_setup() * * Do the multi handle setups that only event-based transfers need. */ static void events_setup(struct Curl_multi *multi, struct events *ev) { /* timer callback */ curl_multi_setopt(multi, CURLMOPT_TIMERFUNCTION, events_timer); curl_multi_setopt(multi, CURLMOPT_TIMERDATA, ev); /* socket callback */ curl_multi_setopt(multi, CURLMOPT_SOCKETFUNCTION, events_socket); curl_multi_setopt(multi, CURLMOPT_SOCKETDATA, ev); } /* wait_or_timeout() * * waits for activity on any of the given sockets, or the timeout to trigger. */ static CURLcode wait_or_timeout(struct Curl_multi *multi, struct events *ev) { bool done = FALSE; CURLMcode mcode = CURLM_OK; CURLcode result = CURLE_OK; while(!done) { CURLMsg *msg; struct socketmonitor *m; struct pollfd *f; struct pollfd fds[4]; int numfds = 0; int pollrc; int i; struct curltime before; struct curltime after; /* populate the fds[] array */ for(m = ev->list, f = &fds[0]; m; m = m->next) { f->fd = m->socket.fd; f->events = m->socket.events; f->revents = 0; /* fprintf(stderr, "poll() %d check socket %d\n", numfds, f->fd); */ f++; numfds++; } /* get the time stamp to use to figure out how long poll takes */ before = Curl_now(); /* wait for activity or timeout */ pollrc = Curl_poll(fds, numfds, ev->ms); after = Curl_now(); ev->msbump = FALSE; /* reset here */ if(0 == pollrc) { /* timeout! */ ev->ms = 0; /* fprintf(stderr, "call curl_multi_socket_action(TIMEOUT)\n"); */ mcode = curl_multi_socket_action(multi, CURL_SOCKET_TIMEOUT, 0, &ev->running_handles); } else if(pollrc > 0) { /* loop over the monitored sockets to see which ones had activity */ for(i = 0; i< numfds; i++) { if(fds[i].revents) { /* socket activity, tell libcurl */ int act = poll2cselect(fds[i].revents); /* convert */ infof(multi->easyp, "call curl_multi_socket_action(socket %d)", fds[i].fd); mcode = curl_multi_socket_action(multi, fds[i].fd, act, &ev->running_handles); } } if(!ev->msbump) { /* If nothing updated the timeout, we decrease it by the spent time. * If it was updated, it has the new timeout time stored already. */ timediff_t timediff = Curl_timediff(after, before); if(timediff > 0) { if(timediff > ev->ms) ev->ms = 0; else ev->ms -= (long)timediff; } } } else return CURLE_RECV_ERROR; if(mcode) return CURLE_URL_MALFORMAT; /* we don't really care about the "msgs_in_queue" value returned in the second argument */ msg = curl_multi_info_read(multi, &pollrc); if(msg) { result = msg->data.result; done = TRUE; } } return result; } /* easy_events() * * Runs a transfer in a blocking manner using the events-based API */ static CURLcode easy_events(struct Curl_multi *multi) { /* this struct is made static to allow it to be used after this function returns and curl_multi_remove_handle() is called */ static struct events evs = {2, FALSE, 0, NULL, 0}; /* if running event-based, do some further multi inits */ events_setup(multi, &evs); return wait_or_timeout(multi, &evs); } #else /* CURLDEBUG */ /* when not built with debug, this function doesn't exist */ #define easy_events(x) CURLE_NOT_BUILT_IN #endif static CURLcode easy_transfer(struct Curl_multi *multi) { bool done = FALSE; CURLMcode mcode = CURLM_OK; CURLcode result = CURLE_OK; while(!done && !mcode) { int still_running = 0; mcode = curl_multi_poll(multi, NULL, 0, 1000, NULL); if(!mcode) mcode = curl_multi_perform(multi, &still_running); /* only read 'still_running' if curl_multi_perform() return OK */ if(!mcode && !still_running) { int rc; CURLMsg *msg = curl_multi_info_read(multi, &rc); if(msg) { result = msg->data.result; done = TRUE; } } } /* Make sure to return some kind of error if there was a multi problem */ if(mcode) { result = (mcode == CURLM_OUT_OF_MEMORY) ? CURLE_OUT_OF_MEMORY : /* The other multi errors should never happen, so return something suitably generic */ CURLE_BAD_FUNCTION_ARGUMENT; } return result; } /* * easy_perform() is the external interface that performs a blocking * transfer as previously setup. * * CONCEPT: This function creates a multi handle, adds the easy handle to it, * runs curl_multi_perform() until the transfer is done, then detaches the * easy handle, destroys the multi handle and returns the easy handle's return * code. * * REALITY: it can't just create and destroy the multi handle that easily. It * needs to keep it around since if this easy handle is used again by this * function, the same multi handle must be re-used so that the same pools and * caches can be used. * * DEBUG: if 'events' is set TRUE, this function will use a replacement engine * instead of curl_multi_perform() and use curl_multi_socket_action(). */ static CURLcode easy_perform(struct Curl_easy *data, bool events) { struct Curl_multi *multi; CURLMcode mcode; CURLcode result = CURLE_OK; SIGPIPE_VARIABLE(pipe_st); if(!data) return CURLE_BAD_FUNCTION_ARGUMENT; if(data->set.errorbuffer) /* clear this as early as possible */ data->set.errorbuffer[0] = 0; if(data->multi) { failf(data, "easy handle already used in multi handle"); return CURLE_FAILED_INIT; } if(data->multi_easy) multi = data->multi_easy; else { /* this multi handle will only ever have a single easy handled attached to it, so make it use minimal hashes */ multi = Curl_multi_handle(1, 3); if(!multi) return CURLE_OUT_OF_MEMORY; data->multi_easy = multi; } if(multi->in_callback) return CURLE_RECURSIVE_API_CALL; /* Copy the MAXCONNECTS option to the multi handle */ curl_multi_setopt(multi, CURLMOPT_MAXCONNECTS, data->set.maxconnects); mcode = curl_multi_add_handle(multi, data); if(mcode) { curl_multi_cleanup(multi); data->multi_easy = NULL; if(mcode == CURLM_OUT_OF_MEMORY) return CURLE_OUT_OF_MEMORY; return CURLE_FAILED_INIT; } sigpipe_ignore(data, &pipe_st); /* run the transfer */ result = events ? easy_events(multi) : easy_transfer(multi); /* ignoring the return code isn't nice, but atm we can't really handle a failure here, room for future improvement! */ (void)curl_multi_remove_handle(multi, data); sigpipe_restore(&pipe_st); /* The multi handle is kept alive, owned by the easy handle */ return result; } /* * curl_easy_perform() is the external interface that performs a blocking * transfer as previously setup. */ CURLcode curl_easy_perform(struct Curl_easy *data) { return easy_perform(data, FALSE); } #ifdef CURLDEBUG /* * curl_easy_perform_ev() is the external interface that performs a blocking * transfer using the event-based API internally. */ CURLcode curl_easy_perform_ev(struct Curl_easy *data) { return easy_perform(data, TRUE); } #endif /* * curl_easy_cleanup() is the external interface to cleaning/freeing the given * easy handle. */ void curl_easy_cleanup(struct Curl_easy *data) { SIGPIPE_VARIABLE(pipe_st); if(!data) return; sigpipe_ignore(data, &pipe_st); Curl_close(&data); sigpipe_restore(&pipe_st); } /* * curl_easy_getinfo() is an external interface that allows an app to retrieve * information from a performed transfer and similar. */ #undef curl_easy_getinfo CURLcode curl_easy_getinfo(struct Curl_easy *data, CURLINFO info, ...) { va_list arg; void *paramp; CURLcode result; va_start(arg, info); paramp = va_arg(arg, void *); result = Curl_getinfo(data, info, paramp); va_end(arg); return result; } static CURLcode dupset(struct Curl_easy *dst, struct Curl_easy *src) { CURLcode result = CURLE_OK; enum dupstring i; enum dupblob j; /* Copy src->set into dst->set first, then deal with the strings afterwards */ dst->set = src->set; Curl_mime_initpart(&dst->set.mimepost, dst); /* clear all string pointers first */ memset(dst->set.str, 0, STRING_LAST * sizeof(char *)); /* duplicate all strings */ for(i = (enum dupstring)0; i< STRING_LASTZEROTERMINATED; i++) { result = Curl_setstropt(&dst->set.str[i], src->set.str[i]); if(result) return result; } /* clear all blob pointers first */ memset(dst->set.blobs, 0, BLOB_LAST * sizeof(struct curl_blob *)); /* duplicate all blobs */ for(j = (enum dupblob)0; j < BLOB_LAST; j++) { result = Curl_setblobopt(&dst->set.blobs[j], src->set.blobs[j]); if(result) return result; } /* duplicate memory areas pointed to */ i = STRING_COPYPOSTFIELDS; if(src->set.postfieldsize && src->set.str[i]) { /* postfieldsize is curl_off_t, Curl_memdup() takes a size_t ... */ dst->set.str[i] = Curl_memdup(src->set.str[i], curlx_sotouz(src->set.postfieldsize)); if(!dst->set.str[i]) return CURLE_OUT_OF_MEMORY; /* point to the new copy */ dst->set.postfields = dst->set.str[i]; } /* Duplicate mime data. */ result = Curl_mime_duppart(&dst->set.mimepost, &src->set.mimepost); if(src->set.resolve) dst->state.resolve = dst->set.resolve; return result; } /* * curl_easy_duphandle() is an external interface to allow duplication of a * given input easy handle. The returned handle will be a new working handle * with all options set exactly as the input source handle. */ struct Curl_easy *curl_easy_duphandle(struct Curl_easy *data) { struct Curl_easy *outcurl = calloc(1, sizeof(struct Curl_easy)); if(NULL == outcurl) goto fail; /* * We setup a few buffers we need. We should probably make them * get setup on-demand in the code, as that would probably decrease * the likeliness of us forgetting to init a buffer here in the future. */ outcurl->set.buffer_size = data->set.buffer_size; /* copy all userdefined values */ if(dupset(outcurl, data)) goto fail; Curl_dyn_init(&outcurl->state.headerb, CURL_MAX_HTTP_HEADER); /* the connection cache is setup on demand */ outcurl->state.conn_cache = NULL; outcurl->state.lastconnect_id = -1; outcurl->progress.flags = data->progress.flags; outcurl->progress.callback = data->progress.callback; if(data->cookies) { /* If cookies are enabled in the parent handle, we enable them in the clone as well! */ outcurl->cookies = Curl_cookie_init(data, data->cookies->filename, outcurl->cookies, data->set.cookiesession); if(!outcurl->cookies) goto fail; } /* duplicate all values in 'change' */ if(data->state.cookielist) { outcurl->state.cookielist = Curl_slist_duplicate(data->state.cookielist); if(!outcurl->state.cookielist) goto fail; } if(data->state.url) { outcurl->state.url = strdup(data->state.url); if(!outcurl->state.url) goto fail; outcurl->state.url_alloc = TRUE; } if(data->state.referer) { outcurl->state.referer = strdup(data->state.referer); if(!outcurl->state.referer) goto fail; outcurl->state.referer_alloc = TRUE; } /* Reinitialize an SSL engine for the new handle * note: the engine name has already been copied by dupset */ if(outcurl->set.str[STRING_SSL_ENGINE]) { if(Curl_ssl_set_engine(outcurl, outcurl->set.str[STRING_SSL_ENGINE])) goto fail; } #ifdef USE_ALTSVC if(data->asi) { outcurl->asi = Curl_altsvc_init(); if(!outcurl->asi) goto fail; if(outcurl->set.str[STRING_ALTSVC]) (void)Curl_altsvc_load(outcurl->asi, outcurl->set.str[STRING_ALTSVC]); } #endif #ifndef CURL_DISABLE_HSTS if(data->hsts) { outcurl->hsts = Curl_hsts_init(); if(!outcurl->hsts) goto fail; if(outcurl->set.str[STRING_HSTS]) (void)Curl_hsts_loadfile(outcurl, outcurl->hsts, outcurl->set.str[STRING_HSTS]); (void)Curl_hsts_loadcb(outcurl, outcurl->hsts); } #endif /* Clone the resolver handle, if present, for the new handle */ if(Curl_resolver_duphandle(outcurl, &outcurl->state.async.resolver, data->state.async.resolver)) goto fail; #ifdef USE_ARES { CURLcode rc; rc = Curl_set_dns_servers(outcurl, data->set.str[STRING_DNS_SERVERS]); if(rc && rc != CURLE_NOT_BUILT_IN) goto fail; rc = Curl_set_dns_interface(outcurl, data->set.str[STRING_DNS_INTERFACE]); if(rc && rc != CURLE_NOT_BUILT_IN) goto fail; rc = Curl_set_dns_local_ip4(outcurl, data->set.str[STRING_DNS_LOCAL_IP4]); if(rc && rc != CURLE_NOT_BUILT_IN) goto fail; rc = Curl_set_dns_local_ip6(outcurl, data->set.str[STRING_DNS_LOCAL_IP6]); if(rc && rc != CURLE_NOT_BUILT_IN) goto fail; } #endif /* USE_ARES */ Curl_convert_setup(outcurl); Curl_initinfo(outcurl); outcurl->magic = CURLEASY_MAGIC_NUMBER; /* we reach this point and thus we are OK */ return outcurl; fail: if(outcurl) { curl_slist_free_all(outcurl->state.cookielist); outcurl->state.cookielist = NULL; Curl_safefree(outcurl->state.buffer); Curl_dyn_free(&outcurl->state.headerb); Curl_safefree(outcurl->state.url); Curl_safefree(outcurl->state.referer); Curl_altsvc_cleanup(&outcurl->asi); Curl_hsts_cleanup(&outcurl->hsts); Curl_freeset(outcurl); free(outcurl); } return NULL; } /* * curl_easy_reset() is an external interface that allows an app to re- * initialize a session handle to the default values. */ void curl_easy_reset(struct Curl_easy *data) { Curl_free_request_state(data); /* zero out UserDefined data: */ Curl_freeset(data); memset(&data->set, 0, sizeof(struct UserDefined)); (void)Curl_init_userdefined(data); /* zero out Progress data: */ memset(&data->progress, 0, sizeof(struct Progress)); /* zero out PureInfo data: */ Curl_initinfo(data); data->progress.flags |= PGRS_HIDE; data->state.current_speed = -1; /* init to negative == impossible */ data->state.retrycount = 0; /* reset the retry counter */ /* zero out authentication data: */ memset(&data->state.authhost, 0, sizeof(struct auth)); memset(&data->state.authproxy, 0, sizeof(struct auth)); #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_CRYPTO_AUTH) Curl_http_auth_cleanup_digest(data); #endif } /* * curl_easy_pause() allows an application to pause or unpause a specific * transfer and direction. This function sets the full new state for the * current connection this easy handle operates on. * * NOTE: if you have the receiving paused and you call this function to remove * the pausing, you may get your write callback called at this point. * * Action is a bitmask consisting of CURLPAUSE_* bits in curl/curl.h * * NOTE: This is one of few API functions that are allowed to be called from * within a callback. */ CURLcode curl_easy_pause(struct Curl_easy *data, int action) { struct SingleRequest *k; CURLcode result = CURLE_OK; int oldstate; int newstate; if(!GOOD_EASY_HANDLE(data) || !data->conn) /* crazy input, don't continue */ return CURLE_BAD_FUNCTION_ARGUMENT; k = &data->req; oldstate = k->keepon & (KEEP_RECV_PAUSE| KEEP_SEND_PAUSE); /* first switch off both pause bits then set the new pause bits */ newstate = (k->keepon &~ (KEEP_RECV_PAUSE| KEEP_SEND_PAUSE)) | ((action & CURLPAUSE_RECV)?KEEP_RECV_PAUSE:0) | ((action & CURLPAUSE_SEND)?KEEP_SEND_PAUSE:0); if((newstate & (KEEP_RECV_PAUSE| KEEP_SEND_PAUSE)) == oldstate) { /* Not changing any pause state, return */ DEBUGF(infof(data, "pause: no change, early return")); return CURLE_OK; } /* Unpause parts in active mime tree. */ if((k->keepon & ~newstate & KEEP_SEND_PAUSE) && (data->mstate == MSTATE_PERFORMING || data->mstate == MSTATE_RATELIMITING) && data->state.fread_func == (curl_read_callback) Curl_mime_read) { Curl_mime_unpause(data->state.in); } /* put it back in the keepon */ k->keepon = newstate; if(!(newstate & KEEP_RECV_PAUSE)) { Curl_http2_stream_pause(data, FALSE); if(data->state.tempcount) { /* there are buffers for sending that can be delivered as the receive pausing is lifted! */ unsigned int i; unsigned int count = data->state.tempcount; struct tempbuf writebuf[3]; /* there can only be three */ /* copy the structs to allow for immediate re-pausing */ for(i = 0; i < data->state.tempcount; i++) { writebuf[i] = data->state.tempwrite[i]; Curl_dyn_init(&data->state.tempwrite[i].b, DYN_PAUSE_BUFFER); } data->state.tempcount = 0; for(i = 0; i < count; i++) { /* even if one function returns error, this loops through and frees all buffers */ if(!result) result = Curl_client_write(data, writebuf[i].type, Curl_dyn_ptr(&writebuf[i].b), Curl_dyn_len(&writebuf[i].b)); Curl_dyn_free(&writebuf[i].b); } if(result) return result; } } /* if there's no error and we're not pausing both directions, we want to have this handle checked soon */ if((newstate & (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) != (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) { Curl_expire(data, 0, EXPIRE_RUN_NOW); /* get this handle going again */ /* reset the too-slow time keeper */ data->state.keeps_speed.tv_sec = 0; if(!data->state.tempcount) /* if not pausing again, force a recv/send check of this connection as the data might've been read off the socket already */ data->conn->cselect_bits = CURL_CSELECT_IN | CURL_CSELECT_OUT; if(data->multi) Curl_update_timer(data->multi); } if(!data->state.done) /* This transfer may have been moved in or out of the bundle, update the corresponding socket callback, if used */ Curl_updatesocket(data); return result; } static CURLcode easy_connection(struct Curl_easy *data, curl_socket_t *sfd, struct connectdata **connp) { if(!data) return CURLE_BAD_FUNCTION_ARGUMENT; /* only allow these to be called on handles with CURLOPT_CONNECT_ONLY */ if(!data->set.connect_only) { failf(data, "CONNECT_ONLY is required!"); return CURLE_UNSUPPORTED_PROTOCOL; } *sfd = Curl_getconnectinfo(data, connp); if(*sfd == CURL_SOCKET_BAD) { failf(data, "Failed to get recent socket"); return CURLE_UNSUPPORTED_PROTOCOL; } return CURLE_OK; } /* * Receives data from the connected socket. Use after successful * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. * Returns CURLE_OK on success, error code on error. */ CURLcode curl_easy_recv(struct Curl_easy *data, void *buffer, size_t buflen, size_t *n) { curl_socket_t sfd; CURLcode result; ssize_t n1; struct connectdata *c; if(Curl_is_in_callback(data)) return CURLE_RECURSIVE_API_CALL; result = easy_connection(data, &sfd, &c); if(result) return result; if(!data->conn) /* on first invoke, the transfer has been detached from the connection and needs to be reattached */ Curl_attach_connnection(data, c); *n = 0; result = Curl_read(data, sfd, buffer, buflen, &n1); if(result) return result; *n = (size_t)n1; return CURLE_OK; } /* * Sends data over the connected socket. Use after successful * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. */ CURLcode curl_easy_send(struct Curl_easy *data, const void *buffer, size_t buflen, size_t *n) { curl_socket_t sfd; CURLcode result; ssize_t n1; struct connectdata *c = NULL; SIGPIPE_VARIABLE(pipe_st); if(Curl_is_in_callback(data)) return CURLE_RECURSIVE_API_CALL; result = easy_connection(data, &sfd, &c); if(result) return result; if(!data->conn) /* on first invoke, the transfer has been detached from the connection and needs to be reattached */ Curl_attach_connnection(data, c); *n = 0; sigpipe_ignore(data, &pipe_st); result = Curl_write(data, sfd, buffer, buflen, &n1); sigpipe_restore(&pipe_st); if(n1 == -1) return CURLE_SEND_ERROR; /* detect EAGAIN */ if(!result && !n1) return CURLE_AGAIN; *n = (size_t)n1; return result; } /* * Wrapper to call functions in Curl_conncache_foreach() * * Returns always 0. */ static int conn_upkeep(struct Curl_easy *data, struct connectdata *conn, void *param) { /* Param is unused. */ (void)param; if(conn->handler->connection_check) /* Do a protocol-specific keepalive check on the connection. */ conn->handler->connection_check(data, conn, CONNCHECK_KEEPALIVE); return 0; /* continue iteration */ } static CURLcode upkeep(struct conncache *conn_cache, void *data) { /* Loop over every connection and make connection alive. */ Curl_conncache_foreach(data, conn_cache, data, conn_upkeep); return CURLE_OK; } /* * Performs connection upkeep for the given session handle. */ CURLcode curl_easy_upkeep(struct Curl_easy *data) { /* Verify that we got an easy handle we can work with. */ if(!GOOD_EASY_HANDLE(data)) return CURLE_BAD_FUNCTION_ARGUMENT; if(data->multi_easy) { /* Use the common function to keep connections alive. */ return upkeep(&data->multi_easy->conn_cache, data); } else { /* No connections, so just return success */ return CURLE_OK; } }