/*************************************************************************** * _ _ ____ _ * Project ___| | | | _ \| | * / __| | | | |_) | | * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * * Copyright (C) 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. * * SPDX-License-Identifier: curl * ***************************************************************************/ #include "curl_setup.h" #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 "cfilters.h" #include "sendf.h" /* for failf function prototype */ #include "connect.h" /* for Curl_getconnectinfo */ #include "slist.h" #include "mime.h" #include "amigaos.h" #include "macos.h" #include "warnless.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" #include "easy_lock.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 easy_init_flags; #ifdef GLOBAL_INIT_IS_THREADSAFE static curl_simple_lock s_lock = CURL_SIMPLE_LOCK_INIT; #define global_init_lock() curl_simple_lock_lock(&s_lock) #define global_init_unlock() curl_simple_lock_unlock(&s_lock) #else #define global_init_lock() #define global_init_unlock() #endif /* * 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 Curl_strdup #else #define system_strdup strdup #endif #if defined(_MSC_VER) && defined(_DLL) # pragma warning(push) # 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; #endif #if defined(_MSC_VER) && defined(_DLL) # pragma warning(pop) #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_trc_init()) { DEBUGF(fprintf(stderr, "Error: Curl_trc_init failed\n")); goto fail; } if(!Curl_ssl_init()) { DEBUGF(fprintf(stderr, "Error: Curl_ssl_init failed\n")); goto fail; } if(Curl_win32_init(flags)) { DEBUGF(fprintf(stderr, "Error: win32_init failed\n")); goto fail; } if(Curl_amiga_init()) { DEBUGF(fprintf(stderr, "Error: Curl_amiga_init failed\n")); goto fail; } if(Curl_macos_init()) { DEBUGF(fprintf(stderr, "Error: Curl_macos_init failed\n")); goto fail; } if(Curl_resolver_global_init()) { DEBUGF(fprintf(stderr, "Error: resolver_global_init failed\n")); goto fail; } if(Curl_ssh_init()) { DEBUGF(fprintf(stderr, "Error: Curl_ssh_init failed\n")); goto fail; } easy_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) { CURLcode result; global_init_lock(); result = global_init(flags, TRUE); global_init_unlock(); return result; } /* * 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) { CURLcode result; /* Invalid input, return immediately */ if(!m || !f || !r || !s || !c) return CURLE_FAILED_INIT; global_init_lock(); if(initialized) { /* Already initialized, do not do it again, but bump the variable anyway to work like curl_global_init() and require the same amount of cleanup calls. */ initialized++; global_init_unlock(); 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 */ result = global_init(flags, FALSE); global_init_unlock(); return result; } /** * curl_global_cleanup() globally cleanups curl, uses the value of * "easy_init_flags" to determine what needs to be cleaned up and what does * not. */ void curl_global_cleanup(void) { global_init_lock(); if(!initialized) { global_init_unlock(); return; } if(--initialized) { global_init_unlock(); return; } Curl_ssl_cleanup(); Curl_resolver_global_cleanup(); #ifdef _WIN32 Curl_win32_cleanup(easy_init_flags); #endif Curl_amiga_cleanup(); Curl_ssh_cleanup(); #ifdef DEBUGBUILD free(leakpointer); #endif easy_init_flags = 0; global_init_unlock(); } /** * curl_global_trace() globally initializes curl logging. */ CURLcode curl_global_trace(const char *config) { #ifndef CURL_DISABLE_VERBOSE_STRINGS CURLcode result; global_init_lock(); result = Curl_trc_opt(config); global_init_unlock(); return result; #else (void)config; return CURLE_OK; #endif } /* * curl_global_sslset() globally initializes the SSL backend to use. */ CURLsslset curl_global_sslset(curl_sslbackend id, const char *name, const curl_ssl_backend ***avail) { CURLsslset rc; global_init_lock(); rc = Curl_init_sslset_nolock(id, name, avail); global_init_unlock(); return rc; } /* * 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 */ global_init_lock(); if(!initialized) { result = global_init(CURL_GLOBAL_DEFAULT, TRUE); if(result) { /* something in the global init failed, return nothing */ DEBUGF(fprintf(stderr, "Error: curl_global_init failed\n")); global_init_unlock(); return NULL; } } global_init_unlock(); /* 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 DEBUGBUILD 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 */ }; #define DEBUG_EV_POLL 0 /* 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 DEBUG_EV_POLL fprintf(stderr, "events_timer: set timeout %ldms\n", timeout_ms); #endif 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; bool found = FALSE; #if defined(CURL_DISABLE_VERBOSE_STRINGS) (void) easy; #endif (void)socketp; m = ev->list; while(m) { if(m->socket.fd == s) { found = TRUE; 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); infof(easy, "socket cb: socket %" FMT_SOCKET_T " 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 %" FMT_SOCKET_T " 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(!found) { if(what == CURL_POLL_REMOVE) { /* should not happen if our logic is correct, but is no drama. */ DEBUGF(infof(easy, "socket cb: asked to REMOVE socket %" FMT_SOCKET_T "but not present!", s)); DEBUGASSERT(0); } 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 %" FMT_SOCKET_T " 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; /* 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; #if DEBUG_EV_POLL fprintf(stderr, "poll() %d check socket %d\n", numfds, f->fd); #endif f++; numfds++; } /* get the time stamp to use to figure out how long poll takes */ before = Curl_now(); if(numfds) { /* wait for activity or timeout */ #if DEBUG_EV_POLL fprintf(stderr, "poll(numfds=%d, timeout=%ldms)\n", numfds, ev->ms); #endif pollrc = Curl_poll(fds, (unsigned int)numfds, ev->ms); #if DEBUG_EV_POLL fprintf(stderr, "poll(numfds=%d, timeout=%ldms) -> %d\n", numfds, ev->ms, pollrc); #endif if(pollrc < 0) return CURLE_UNRECOVERABLE_POLL; } else { #if DEBUG_EV_POLL fprintf(stderr, "poll, but no fds, wait timeout=%ldms\n", ev->ms); #endif pollrc = 0; if(ev->ms > 0) Curl_wait_ms(ev->ms); } ev->msbump = FALSE; /* reset here */ if(!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 { /* here pollrc is > 0 */ struct Curl_llist_node *e = Curl_llist_head(&multi->process); struct Curl_easy *data; DEBUGASSERT(e); data = Curl_node_elem(e); DEBUGASSERT(data); /* 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 */ /* sending infof "randomly" to the first easy handle */ infof(data, "call curl_multi_socket_action(socket " "%" FMT_SOCKET_T ")", (curl_socket_t)fds[i].fd); mcode = curl_multi_socket_action(multi, fds[i].fd, act, &ev->running_handles); } } if(!ev->msbump && ev->ms >= 0) { /* 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(Curl_now(), before); if(timediff > 0) { #if DEBUG_EV_POLL fprintf(stderr, "poll timeout %ldms not updated, decrease by " "time spent %ldms\n", ev->ms, (long)timediff); #endif if(timediff > ev->ms) ev->ms = 0; else ev->ms -= (long)timediff; } } } if(mcode) return CURLE_URL_MALFORMAT; /* we do not 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 = {-1, FALSE, 0, NULL, 0}; /* if running event-based, do some further multi inits */ events_setup(multi, &evs); return wait_or_timeout(multi, &evs); } #else /* DEBUGBUILD */ /* when not built with debug, this function does not 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 cannot 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 reused 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; data->state.os_errno = 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, 7); if(!multi) return CURLE_OUT_OF_MEMORY; } if(multi->in_callback) return CURLE_RECURSIVE_API_CALL; /* Copy the MAXCONNECTS option to the multi handle */ curl_multi_setopt(multi, CURLMOPT_MAXCONNECTS, (long)data->set.maxconnects); data->multi_easy = NULL; /* pretend it does not exist */ mcode = curl_multi_add_handle(multi, data); if(mcode) { curl_multi_cleanup(multi); if(mcode == CURLM_OUT_OF_MEMORY) return CURLE_OUT_OF_MEMORY; return CURLE_FAILED_INIT; } /* assign this after curl_multi_add_handle() */ data->multi_easy = multi; sigpipe_init(&pipe_st); sigpipe_apply(data, &pipe_st); /* run the transfer */ result = events ? easy_events(multi) : easy_transfer(multi); /* ignoring the return code is not nice, but atm we cannot 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 DEBUGBUILD /* * 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) { if(GOOD_EASY_HANDLE(data)) { SIGPIPE_VARIABLE(pipe_st); 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); /* clear all dest string and blob pointers first, in case we error out mid-function */ memset(dst->set.str, 0, STRING_LAST * sizeof(char *)); memset(dst->set.blobs, 0, BLOB_LAST * sizeof(struct curl_blob *)); /* 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; } /* 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.str[i]) { if(src->set.postfieldsize == -1) dst->set.str[i] = strdup(src->set.str[i]); else /* 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, &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(!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 pool is setup on demand */ outcurl->state.lastconnect_id = -1; outcurl->state.recent_conn_id = -1; outcurl->id = -1; outcurl->progress.flags = data->progress.flags; outcurl->progress.callback = data->progress.callback; #ifndef CURL_DISABLE_COOKIES outcurl->state.cookielist = NULL; if(data->cookies && data->state.cookie_engine) { /* If cookies are enabled in the parent handle, we enable them in the clone as well! */ outcurl->cookies = Curl_cookie_init(outcurl, NULL, outcurl->cookies, data->set.cookiesession); if(!outcurl->cookies) goto fail; } if(data->state.cookielist) { outcurl->state.cookielist = Curl_slist_duplicate(data->state.cookielist); if(!outcurl->state.cookielist) goto fail; } #endif 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; } #ifndef CURL_DISABLE_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 #ifdef CURLRES_ASYNCH /* 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; #endif #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 */ #ifndef CURL_DISABLE_HTTP Curl_llist_init(&outcurl->state.httphdrs, NULL); #endif Curl_initinfo(outcurl); outcurl->magic = CURLEASY_MAGIC_NUMBER; /* we reach this point and thus we are OK */ return outcurl; fail: if(outcurl) { #ifndef CURL_DISABLE_COOKIES free(outcurl->cookies); #endif Curl_dyn_free(&outcurl->state.headerb); 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_req_hard_reset(&data->req, 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_DIGEST_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; bool recursive = FALSE; bool keep_changed, unpause_read, not_all_paused; if(!GOOD_EASY_HANDLE(data) || !data->conn) /* crazy input, do not continue */ return CURLE_BAD_FUNCTION_ARGUMENT; if(Curl_is_in_callback(data)) recursive = TRUE; 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); keep_changed = ((newstate & (KEEP_RECV_PAUSE| KEEP_SEND_PAUSE)) != oldstate); not_all_paused = (newstate & (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE)) != (KEEP_RECV_PAUSE|KEEP_SEND_PAUSE); unpause_read = ((k->keepon & ~newstate & KEEP_SEND_PAUSE) && (data->mstate == MSTATE_PERFORMING || data->mstate == MSTATE_RATELIMITING)); /* Unpausing writes is detected on the next run in * transfer.c:Curl_sendrecv(). This is because this may result * in a transfer error if the application's callbacks fail */ /* Set the new keepon state, so it takes effect no matter what error * may happen afterwards. */ k->keepon = newstate; /* If not completely pausing both directions now, run again in any case. */ if(not_all_paused) { Curl_expire(data, 0, EXPIRE_RUN_NOW); /* reset the too-slow time keeper */ data->state.keeps_speed.tv_sec = 0; /* Simulate socket events on next run for unpaused directions */ if(!(newstate & KEEP_SEND_PAUSE)) data->state.select_bits |= CURL_CSELECT_OUT; if(!(newstate & KEEP_RECV_PAUSE)) data->state.select_bits |= CURL_CSELECT_IN; /* On changes, tell application to update its timers. */ if(keep_changed && data->multi) { if(Curl_update_timer(data->multi)) { result = CURLE_ABORTED_BY_CALLBACK; goto out; } } } if(unpause_read) { result = Curl_creader_unpause(data); if(result) goto out; } if(!(k->keepon & KEEP_RECV_PAUSE) && Curl_cwriter_is_paused(data)) { Curl_conn_ev_data_pause(data, FALSE); result = Curl_cwriter_unpause(data); } out: if(!result && !data->state.done && keep_changed) /* This transfer may have been moved in or out of the bundle, update the corresponding socket callback, if used */ result = Curl_updatesocket(data); if(recursive) /* this might have called a callback recursively which might have set this to false again on exit */ Curl_set_in_callback(data, TRUE); return result; } static CURLcode easy_connection(struct Curl_easy *data, struct connectdata **connp) { curl_socket_t sfd; 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) { CURLcode result; ssize_t n1; struct connectdata *c; if(Curl_is_in_callback(data)) return CURLE_RECURSIVE_API_CALL; result = easy_connection(data, &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_connection(data, c); *n = 0; result = Curl_conn_recv(data, FIRSTSOCKET, buffer, buflen, &n1); if(result) return result; *n = (size_t)n1; return CURLE_OK; } #ifdef USE_WEBSOCKETS CURLcode Curl_connect_only_attach(struct Curl_easy *data) { CURLcode result; struct connectdata *c = NULL; result = easy_connection(data, &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_connection(data, c); return CURLE_OK; } #endif /* USE_WEBSOCKETS */ /* * Sends data over the connected socket. * * This is the private internal version of curl_easy_send() */ CURLcode Curl_senddata(struct Curl_easy *data, const void *buffer, size_t buflen, size_t *n) { CURLcode result; struct connectdata *c = NULL; SIGPIPE_VARIABLE(pipe_st); *n = 0; result = easy_connection(data, &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_connection(data, c); sigpipe_ignore(data, &pipe_st); result = Curl_conn_send(data, FIRSTSOCKET, buffer, buflen, FALSE, n); sigpipe_restore(&pipe_st); if(result && result != CURLE_AGAIN) return CURLE_SEND_ERROR; return result; } /* * 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) { size_t written = 0; CURLcode result; if(Curl_is_in_callback(data)) return CURLE_RECURSIVE_API_CALL; result = Curl_senddata(data, buffer, buflen, &written); *n = written; return result; } /* * 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(Curl_is_in_callback(data)) return CURLE_RECURSIVE_API_CALL; /* Use the common function to keep connections alive. */ return Curl_cpool_upkeep(data); }