diff --git a/lib/Makefile.inc b/lib/Makefile.inc index 266ba52af3..aff75e28a5 100644 --- a/lib/Makefile.inc +++ b/lib/Makefile.inc @@ -154,6 +154,7 @@ LIB_VSSH_HFILES = \ LIB_CFILES = \ altsvc.c \ amigaos.c \ + api.c \ asyn-ares.c \ asyn-base.c \ asyn-thrdd.c \ @@ -289,6 +290,7 @@ LIB_CFILES = \ LIB_HFILES = \ altsvc.h \ amigaos.h \ + api.h \ arpa_telnet.h \ asyn.h \ bufq.h \ diff --git a/lib/api.c b/lib/api.c new file mode 100644 index 0000000000..adcc51b3f8 --- /dev/null +++ b/lib/api.c @@ -0,0 +1,398 @@ +/*************************************************************************** + * _ _ ____ _ + * 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" + +#include "urldata.h" +#include "api.h" +#include "multiif.h" + +struct Curl_eapi_fn_props { + Curl_eapi_fn fn; + uint8_t data_is_killed; /* easy handle is killed in call */ + uint8_t recurse; /* may be called when another call is in progress */ + uint8_t no_event_cb; /* may not be called during a multi event callback */ +}; + +static const struct Curl_eapi_fn_props eapi_fn_props[CURL_EAPI_FN_LAST] = { + /* function kill rec !ev */ + { CURL_EAPI_FN_easy_cleanup, 1, 0, 0 }, + { CURL_EAPI_FN_easy_duphandle, 0, 1, 0 }, + { CURL_EAPI_FN_easy_getinfo, 0, 1, 0 }, + { CURL_EAPI_FN_easy_pause, 0, 1, 1 }, + { CURL_EAPI_FN_easy_perform_ev, 0, 0, 0 }, + { CURL_EAPI_FN_easy_perform, 0, 0, 0 }, + { CURL_EAPI_FN_easy_recv, 0, 0, 0 }, + { CURL_EAPI_FN_easy_reset, 0, 0, 0 }, + { CURL_EAPI_FN_easy_send, 0, 0, 0 }, + { CURL_EAPI_FN_easy_setopt, 0, 1, 0 }, + { CURL_EAPI_FN_easy_ssls_export, 0, 0, 0 }, + { CURL_EAPI_FN_easy_ssls_import, 0, 0, 0 }, + { CURL_EAPI_FN_easy_upkeep, 0, 0, 0 }, + { CURL_EAPI_FN_ws_recv, 0, 1, 0 }, + { CURL_EAPI_FN_ws_send, 0, 1, 0 }, + { CURL_EAPI_FN_ws_start_frame, 0, 1, 0 }, +}; + +struct Curl_mapi_fn_props { + Curl_mapi_fn fn; + uint8_t multi_is_killed; /* multi handle is killed during call */ + uint8_t recurse; /* may be called when another call is in progress */ + uint8_t allow_ntfy_cb; /* may be called during a notify callback */ +}; + +static const struct Curl_mapi_fn_props mapi_fn_props[CURL_MAPI_FN_LAST] = { + /* function kill rec ntfy */ + { CURL_MAPI_FN_multi_add_handle, 0, 0, 1 }, + { CURL_MAPI_FN_multi_assign, 0, 1, 1 }, + { CURL_MAPI_FN_multi_cleanup, 1, 0, 0 }, + { CURL_MAPI_FN_multi_fdset, 0, 0, 1 }, + { CURL_MAPI_FN_multi_get_handles, 0, 1, 1 }, + { CURL_MAPI_FN_multi_get_offt, 0, 1, 1 }, + { CURL_MAPI_FN_multi_info_read, 0, 1, 1 }, + { CURL_MAPI_FN_multi_notify_disable, 0, 1, 1 }, + { CURL_MAPI_FN_multi_notify_enable, 0, 1, 1 }, + { CURL_MAPI_FN_multi_perform, 0, 0, 0 }, + { CURL_MAPI_FN_multi_poll, 0, 0, 1 }, + { CURL_MAPI_FN_multi_remove_handle, 0, 0, 1 }, + { CURL_MAPI_FN_multi_setopt, 0, 0, 1 }, + { CURL_MAPI_FN_multi_socket_action, 0, 0, 0 }, + { CURL_MAPI_FN_multi_socket_all, 0, 0, 0 }, + { CURL_MAPI_FN_multi_socket, 0, 0, 0 }, + { CURL_MAPI_FN_multi_timeout, 0, 0, 1 }, + { CURL_MAPI_FN_multi_wait, 0, 0, 1 }, + { CURL_MAPI_FN_multi_waitfds, 0, 0, 1 }, +}; + +struct Curl_cbapi_fn_props { + Curl_cbapi_fn fn; + uint8_t is_event_cb; /* is a multi event processing callback */ +}; + +static const struct Curl_cbapi_fn_props +cbapi_fn_props[CURL_CBAPI_FN_LAST - CURL_CBAPI_FN_START] = { + { CURL_CBAPI_FN_easy_chunk_bgn, 0 }, + { CURL_CBAPI_FN_easy_chunk_end, 0 }, + { CURL_CBAPI_FN_easy_closesocket, 0 }, + { CURL_CBAPI_FN_easy_cr_in_read, 0 }, + { CURL_CBAPI_FN_easy_cr_in_resume_from, 0 }, + { CURL_CBAPI_FN_easy_cw_out_cb, 0 }, + { CURL_CBAPI_FN_easy_fdebug, 0 }, + { CURL_CBAPI_FN_easy_fnmatch_data, 0 }, + { CURL_CBAPI_FN_easy_fopensocket, 0 }, + { CURL_CBAPI_FN_easy_fprereq, 0 }, + { CURL_CBAPI_FN_easy_fprogress, 0 }, + { CURL_CBAPI_FN_easy_fread_func, 0 }, + { CURL_CBAPI_FN_easy_fsockopt, 0 }, + { CURL_CBAPI_FN_easy_fsslctx, 0 }, + { CURL_CBAPI_FN_easy_fwrite_rtp, 0 }, + { CURL_CBAPI_FN_easy_fxferinfo, 0 }, + { CURL_CBAPI_FN_easy_ioctl_func, 0 }, + { CURL_CBAPI_FN_easy_resolver_start, 0 }, + { CURL_CBAPI_FN_easy_seek_func, 0 }, + { CURL_CBAPI_FN_easy_ssh_hostkeyfunc, 0 }, + { CURL_CBAPI_FN_easy_ssh_keyfunc, 0 }, + { CURL_CBAPI_FN_easy_trailer_callback, 0 }, + + { CURL_CBAPI_FN_multi_ntfy_cb, 0 }, + { CURL_CBAPI_FN_multi_push_cb, 0 }, + { CURL_CBAPI_FN_multi_socket_cb, 1 }, + { CURL_CBAPI_FN_multi_timer_cb, 1 }, +}; + +static bool eapi_in_event_cb(struct Curl_easy *data) +{ + struct Curl_multi *multi = data->multi; + if(multi && multi->callstack.count) { + size_t i; + for(i = 0; i < multi->callstack.count; ++i) { + if(multi->callstack.calls[i] >= CURL_CBAPI_FN_START) { + uint16_t fn = multi->callstack.calls[i]; + if((fn < CURL_CBAPI_FN_LAST) && + cbapi_fn_props[fn - CURL_CBAPI_FN_START].is_event_cb) + return TRUE; + } + } + } + return FALSE; +} + +static bool mapi_in_ntfy_cb(struct Curl_multi *multi) +{ + if(multi && multi->callstack.count) { + size_t i; + for(i = 0; i < multi->callstack.count; ++i) { + if(multi->callstack.calls[i] == CURL_CBAPI_FN_multi_ntfy_cb) + return TRUE; + } + } + return FALSE; +} + +bool Curl_api_multi_is_in_callback(struct Curl_multi *multi) +{ + if(multi && multi->callstack.count) { + size_t i; + for(i = 0; i < multi->callstack.count; ++i) { + if(multi->callstack.calls[i] >= CURL_CBAPI_FN_START) + return TRUE; + } + } + return FALSE; +} + +bool Curl_api_is_in_callback(struct Curl_easy *data) +{ + if(data && data->multi) { + return Curl_api_multi_is_in_callback(data->multi); + } + return FALSE; +} + +bool Curl_eapi_enter(struct Curl_eapi_guard *guard, + CURL *curl, + Curl_eapi_fn fn, + CURLcode *presult) +{ + struct Curl_easy *data = curl; + const struct Curl_eapi_fn_props *fn_props; + CURLcode result = CURLE_OK; + + guard->depth = 0; + + /* Verify that we got an easy handle we can work with. */ + if(!GOOD_EASY_HANDLE(data)) { + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; + } + /* verify that is either not added to a multi handle OR has a + * GOOD multi handle that knows `data` for `data->mid`. */ + if(data->mid != UINT32_MAX) { + if(GOOD_MULTI_HANDLE(data->multi)) { + if(!Curl_multi_knows_easy(data->multi, data)) { + /* But multi does not know it, something is fishy, better deny call */ + DEBUGASSERT(0); + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; + } + } + else { + DEBUGASSERT(0); /* data needs to have a GOOD multi handle */ + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; + } + } + else if(data->multi) { + DEBUGASSERT(0); /* data should not have a multi handle */ + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; + } + /* verify that the call `fn` we're about to enter is known + * and check call properties to be admitting. */ + if(fn >= CURL_EAPI_FN_LAST) { + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; + } + fn_props = &eapi_fn_props[fn]; + DEBUGASSERT(fn_props->fn == fn); + if(!fn_props->recurse) { + if(data->callstack.count) { +#ifdef CURLVERBOSE + DEBUGF(curl_mfprintf(stderr, + "EAPI guard: calling %u with call to %u ongoing\n", (uint16_t)fn, + data->callstack.calls[data->callstack.count-1])); +#endif + result = CURLE_RECURSIVE_API_CALL; + goto out; + } + if(data->multi && data->multi->callstack.count) { +#ifdef CURLVERBOSE + + DEBUGF(curl_mfprintf(stderr, + "EAPI guard: calling %u with multi call to %u ongoing\n", (uint16_t)fn, + data->multi->callstack.calls[data->multi->callstack.count-1])); +#endif + result = CURLE_RECURSIVE_API_CALL; + goto out; + } + } + + if(fn_props->no_event_cb && eapi_in_event_cb(data)) { + /* Not allowed to be invoked while an event cb is ongoing */ +#ifdef CURLVERBOSE + DEBUGF(curl_mfprintf(stderr, + "EAPI guard: calling %u while event callback ongoing\n", + (uint16_t)fn)); +#endif + result = CURLE_RECURSIVE_API_CALL; + goto out; + } + + /* all fine, add to data's callstack */ + if(data->callstack.count >= CURL_EAPI_MAX_RECURSION) { + result = CURLE_RECURSIVE_API_CALL; + goto out; + } + data->callstack.calls[data->callstack.count] = (uint16_t)fn; + ++data->callstack.count; + guard->depth = data->callstack.count; + guard->data = fn_props->data_is_killed ? NULL : data; + +out: + if(presult) + *presult = result; + return guard->depth > 0; +} + +void Curl_eapi_leave(struct Curl_eapi_guard *guard) +{ + if(guard->depth) { + /* guard->data is set when handle is supposed to stay alive during call */ + if(guard->data && GOOD_EASY_HANDLE(guard->data)) { + if(guard->depth > guard->data->callstack.count) { + DEBUGASSERT(0); /* something very wrong */ + } + else { + if(guard->depth < guard->data->callstack.count) { + DEBUGASSERT(0); /* someone forgot to clean up */ + } + /* reset to depth the guard was entered in */ + guard->data->callstack.count = (uint16_t)(guard->depth - 1); + } + } + } +} + +bool Curl_mapi_enter(struct Curl_mapi_guard *guard, + CURLM *m, + Curl_mapi_fn fn, + CURLMcode *pmresult) +{ + struct Curl_multi *multi = m; + const struct Curl_mapi_fn_props *fn_props; + CURLMcode mresult = CURLM_OK; + + guard->depth = 0; + + /* Verify that we got an easy handle we can work with. */ + if(!GOOD_MULTI_HANDLE(multi)) { + mresult = CURLM_BAD_HANDLE; + goto out; + } + if(fn >= CURL_MAPI_FN_LAST) { + mresult = CURLM_BAD_FUNCTION_ARGUMENT; + goto out; + } + fn_props = &mapi_fn_props[fn]; + DEBUGASSERT(fn_props->fn == fn); + if(fn_props->allow_ntfy_cb && mapi_in_ntfy_cb(multi)) { + /* explicitly allowed, even though normal recursion may not */ + } + else if(!fn_props->recurse && multi->callstack.count) { +#ifdef CURLVERBOSE + DEBUGF(curl_mfprintf(stderr, + "MAPI guard: calling %u with call to %u ongoing\n", (uint16_t)fn, + multi->callstack.calls[multi->callstack.count-1])); +#endif + mresult = CURLM_RECURSIVE_API_CALL; + goto out; + } + + /* all fine, add to data's callstack */ + if(multi->callstack.count >= CURL_MAPI_MAX_RECURSION) { + mresult = CURLM_RECURSIVE_API_CALL; + goto out; + } + multi->callstack.calls[multi->callstack.count] = (uint16_t)fn; + ++multi->callstack.count; + guard->depth = multi->callstack.count; + guard->multi = fn_props->multi_is_killed ? NULL : multi; + +out: + if(pmresult) + *pmresult = mresult; + return guard->depth > 0; +} + +void Curl_mapi_leave(struct Curl_mapi_guard *guard) +{ + if(guard->depth) { + /* guard->data is set when handle is supposed to stay alive during call */ + if(guard->multi && GOOD_MULTI_HANDLE(guard->multi)) { + if(guard->depth > guard->multi->callstack.count) { + DEBUGASSERT(0); /* something very wrong */ + } + else { + if(guard->depth < guard->multi->callstack.count) { + DEBUGASSERT(0); /* someone forgot to clean up */ + } + /* reset to depth the guard was entered in */ + guard->multi->callstack.count = (uint16_t)(guard->depth - 1); + } + } + } +} + +void Curl_cbapi_enter(struct Curl_mapi_guard *guard, + struct Curl_easy *data, + struct Curl_multi *multi, + Curl_cbapi_fn fn) +{ + guard->depth = 0; + + if(!multi) + multi = data ? data->multi : NULL; + /* if not multi is involved here, just leave */ + if(!multi) + return; + /* invalid callback specifier? */ + if((fn >= CURL_CBAPI_FN_LAST) || (fn < CURL_CBAPI_FN_START)) { + DEBUGASSERT(0); + return; + } + DEBUGASSERT(cbapi_fn_props[fn - CURL_CBAPI_FN_START].fn == fn); + if(multi->callstack.count) { + size_t i; + for(i = multi->callstack.count; i; --i) { + if(multi->callstack.calls[i - 1] == fn) { + /* recursive invocation of the same callback */ + DEBUGASSERT(0); + return; + } + } + } + + /* all fine, add to data's callstack */ + /* if multi callstack already at max depth, leave */ + if(multi->callstack.count >= CURL_MAPI_MAX_RECURSION) + return; + multi->callstack.calls[multi->callstack.count] = (uint16_t)fn; + ++multi->callstack.count; + guard->depth = multi->callstack.count; + guard->multi = multi; +} + +void Curl_cbapi_leave(struct Curl_mapi_guard *guard) +{ + Curl_mapi_leave(guard); +} diff --git a/lib/api.h b/lib/api.h new file mode 100644 index 0000000000..c6810a392e --- /dev/null +++ b/lib/api.h @@ -0,0 +1,207 @@ +#ifndef HEADER_CURL_API_H +#define HEADER_CURL_API_H +/*************************************************************************** + * _ _ ____ _ + * 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" + +#define CURLEASY_MAGIC_NUMBER 0xc0dedbadU +#ifdef DEBUGBUILD +/* On a debug build, we want to fail hard on easy handles that + * are not NULL, but no longer have the MAGIC touch. This gives + * us early warning on things only discovered by valgrind otherwise. */ +#define GOOD_EASY_HANDLE(x) \ + (((x) && ((x)->magic == CURLEASY_MAGIC_NUMBER)) ? TRUE : \ + (DEBUGASSERT(!(x)), FALSE)) +#else +#define GOOD_EASY_HANDLE(x) \ + ((x) && ((x)->magic == CURLEASY_MAGIC_NUMBER)) +#endif + +#define CURLMULTI_MAGIC_NUMBER 0x000bab1e + +#ifdef DEBUGBUILD +/* On a debug build, we want to fail hard on multi handles that + * are not NULL, but no longer have the MAGIC touch. This gives + * us early warning on things only discovered by valgrind otherwise. */ +#define GOOD_MULTI_HANDLE(x) \ + (((x) && (x)->magic == CURLMULTI_MAGIC_NUMBER) ? TRUE : \ + (DEBUGASSERT(!(x)), FALSE)) +#else +#define GOOD_MULTI_HANDLE(x) \ + ((x) && (x)->magic == CURLMULTI_MAGIC_NUMBER) +#endif + +/* the API functions called on a CURL* */ +typedef enum { + CURL_EAPI_FN_easy_cleanup, + CURL_EAPI_FN_easy_duphandle, + CURL_EAPI_FN_easy_getinfo, + CURL_EAPI_FN_easy_pause, + CURL_EAPI_FN_easy_perform_ev, + CURL_EAPI_FN_easy_perform, + CURL_EAPI_FN_easy_recv, + CURL_EAPI_FN_easy_reset, + CURL_EAPI_FN_easy_send, + CURL_EAPI_FN_easy_setopt, + CURL_EAPI_FN_easy_ssls_export, + CURL_EAPI_FN_easy_ssls_import, + CURL_EAPI_FN_easy_upkeep, + CURL_EAPI_FN_ws_recv, + CURL_EAPI_FN_ws_send, + CURL_EAPI_FN_ws_start_frame, + CURL_EAPI_FN_LAST +} Curl_eapi_fn; + +/* the API functions called on a CURLM* */ +typedef enum { + CURL_MAPI_FN_multi_add_handle, + CURL_MAPI_FN_multi_assign, + CURL_MAPI_FN_multi_cleanup, + CURL_MAPI_FN_multi_fdset, + CURL_MAPI_FN_multi_get_handles, + CURL_MAPI_FN_multi_get_offt, + CURL_MAPI_FN_multi_info_read, + CURL_MAPI_FN_multi_notify_disable, + CURL_MAPI_FN_multi_notify_enable, + CURL_MAPI_FN_multi_perform, + CURL_MAPI_FN_multi_poll, + CURL_MAPI_FN_multi_remove_handle, + CURL_MAPI_FN_multi_setopt, + CURL_MAPI_FN_multi_socket_action, + CURL_MAPI_FN_multi_socket_all, + CURL_MAPI_FN_multi_socket, + CURL_MAPI_FN_multi_timeout, + CURL_MAPI_FN_multi_wait, + CURL_MAPI_FN_multi_waitfds, + CURL_MAPI_FN_LAST +} Curl_mapi_fn; + +#define CURL_CBAPI_FN_START (16 * 1024) + +/* the callback functions */ +typedef enum { + CURL_CBAPI_FN_easy_chunk_bgn = CURL_CBAPI_FN_START, + CURL_CBAPI_FN_easy_chunk_end, + CURL_CBAPI_FN_easy_closesocket, + CURL_CBAPI_FN_easy_cr_in_read, + CURL_CBAPI_FN_easy_cr_in_resume_from, + CURL_CBAPI_FN_easy_cw_out_cb, + CURL_CBAPI_FN_easy_fdebug, + CURL_CBAPI_FN_easy_fnmatch_data, + CURL_CBAPI_FN_easy_fopensocket, + CURL_CBAPI_FN_easy_fprereq, + CURL_CBAPI_FN_easy_fprogress, + CURL_CBAPI_FN_easy_fread_func, + CURL_CBAPI_FN_easy_fsockopt, + CURL_CBAPI_FN_easy_fsslctx, + CURL_CBAPI_FN_easy_fwrite_rtp, + CURL_CBAPI_FN_easy_fxferinfo, + CURL_CBAPI_FN_easy_ioctl_func, + CURL_CBAPI_FN_easy_resolver_start, + CURL_CBAPI_FN_easy_seek_func, + CURL_CBAPI_FN_easy_ssh_hostkeyfunc, + CURL_CBAPI_FN_easy_ssh_keyfunc, + CURL_CBAPI_FN_easy_trailer_callback, + + CURL_CBAPI_FN_multi_ntfy_cb, + CURL_CBAPI_FN_multi_push_cb, + CURL_CBAPI_FN_multi_socket_cb, + CURL_CBAPI_FN_multi_timer_cb, + + CURL_CBAPI_FN_LAST +} Curl_cbapi_fn; + + +#define CURL_EAPI_MAX_RECURSION 7 + +struct Curl_eapi_stack { + uint16_t count; + uint16_t calls[CURL_EAPI_MAX_RECURSION]; +}; + +struct Curl_eapi_guard { + struct Curl_easy *data; /* != NULL if handle stays */ + uint16_t depth; /* > 0 if this guard was entered */ +}; + +bool Curl_eapi_enter(struct Curl_eapi_guard *guard, + CURL *curl, + Curl_eapi_fn fn, + CURLcode *presult); +void Curl_eapi_leave(struct Curl_eapi_guard *guard); + +/* Curl_eapi_enter() checks for curl being NULL, but windows compiler + * analyzers do not realize this. *sigh* */ +#define CURL_EAPI_ENTER(g, curl, fn, r) \ + Curl_eapi_enter((g), (curl), CURL_EAPI_FN_##fn, (r)) && (curl) +#define CURL_EAPI_LEAVE(g) \ + Curl_eapi_leave(g) + + +#define CURL_MAPI_MAX_RECURSION 15 + +struct Curl_mapi_stack { + uint16_t count; + uint16_t calls[CURL_MAPI_MAX_RECURSION]; +}; + +struct Curl_mapi_guard { + struct Curl_multi *multi; /* != NULL if handle stays */ + uint16_t depth; /* > 0 if this guard was entered */ +}; + +bool Curl_mapi_enter(struct Curl_mapi_guard *guard, + CURLM *m, + Curl_mapi_fn fn, + CURLMcode *pmresult); +void Curl_mapi_leave(struct Curl_mapi_guard *guard); + +/* Curl_mapi_enter() checks for m being NULL, but windows compiler + * analyzers do not realize this. *sigh* */ +#define CURL_MAPI_ENTER(g, m, fn, r) \ + Curl_mapi_enter((g), (m), CURL_MAPI_FN_##fn, (r)) && (m) +#define CURL_MAPI_LEAVE(g) \ + Curl_mapi_leave(g) + + +void Curl_cbapi_enter(struct Curl_mapi_guard *guard, + struct Curl_easy *data, + struct Curl_multi *multi, + Curl_cbapi_fn fn); +void Curl_cbapi_leave(struct Curl_mapi_guard *guard); + +#define CURL_CBAPI_START(g, d, fn) \ + Curl_cbapi_enter((g), (d), NULL, CURL_CBAPI_FN_##fn) +#define CURL_CBAPI_MULTI_START(g, m, fn) \ + Curl_cbapi_enter((g), NULL, (m), CURL_CBAPI_FN_##fn) +#define CURL_CBAPI_END(g) \ + Curl_cbapi_leave(g) +#define CURL_CBAPI_MULTI_END(g) \ + Curl_cbapi_leave(g) + + +bool Curl_api_is_in_callback(struct Curl_easy *data); +bool Curl_api_multi_is_in_callback(struct Curl_multi *multi); + +#endif /* HEADER_CURL_API_H */ diff --git a/lib/cf-socket.c b/lib/cf-socket.c index 0741f2ba41..9c65963ce3 100644 --- a/lib/cf-socket.c +++ b/lib/cf-socket.c @@ -430,11 +430,12 @@ static CURLcode socket_open(struct Curl_easy *data, * might have been changed and this 'new' address will actually be used * here to connect. */ - Curl_set_in_callback(data, TRUE); + struct Curl_mapi_guard guard; + CURL_CBAPI_START(&guard, data, easy_fopensocket); *sockfd = data->set.fopensocket(data->set.opensocket_client, CURLSOCKTYPE_IPCXN, (struct curl_sockaddr *)addr); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); } else { /* opensocket callback not set, so create the socket now */ @@ -521,11 +522,12 @@ static int socket_close(struct Curl_easy *data, struct connectdata *conn, return 0; if(use_callback && conn && conn->fclosesocket) { + struct Curl_mapi_guard guard; int rc; Curl_multi_will_close(data, sock); - Curl_set_in_callback(data, TRUE); + CURL_CBAPI_START(&guard, data, easy_closesocket); rc = conn->fclosesocket(conn->closesocket_client, sock); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); return rc; } @@ -1220,11 +1222,12 @@ static CURLcode cf_socket_open(struct Curl_cfilter *cf, if(data->set.fsockopt) { /* activate callback for setting socket options */ - Curl_set_in_callback(data, TRUE); + struct Curl_mapi_guard guard; + CURL_CBAPI_START(&guard, data, easy_fsockopt); error = data->set.fsockopt(data->set.sockopt_client, ctx->sock, CURLSOCKTYPE_IPCXN); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); if(error == CURL_SOCKOPT_ALREADY_CONNECTED) isconnected = TRUE; @@ -2231,13 +2234,14 @@ static CURLcode cf_tcp_accept_connect(struct Curl_cfilter *cf, ctx->sock, ctx->ip.remote_ip, ctx->ip.remote_port); if(data->set.fsockopt) { + struct Curl_mapi_guard guard; int error = 0; /* activate callback for setting socket options */ - Curl_set_in_callback(data, TRUE); + CURL_CBAPI_START(&guard, data, easy_fsockopt); error = data->set.fsockopt(data->set.sockopt_client, ctx->sock, CURLSOCKTYPE_ACCEPT); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); if(error) return CURLE_ABORTED_BY_CALLBACK; diff --git a/lib/curl_trc.c b/lib/curl_trc.c index 91b180f908..1400707a7a 100644 --- a/lib/curl_trc.c +++ b/lib/curl_trc.c @@ -51,11 +51,11 @@ static void trc_write(struct Curl_easy *data, curl_infotype type, { if(data->set.verbose) { if(data->set.fdebug) { - uint8_t inCallback = Curl_is_in_callback(data); - Curl_set_in_callback(data, TRUE); + struct Curl_mapi_guard guard; + CURL_CBAPI_START(&guard, data, easy_fdebug); (void)(*data->set.fdebug)(data, type, CURL_UNCONST(ptr), size, data->set.debugdata); - Curl_set_in_callback(data, inCallback); + CURL_CBAPI_END(&guard); } else { static const char s_infotype[CURLINFO_END][3] = { @@ -133,22 +133,21 @@ void Curl_debug(struct Curl_easy *data, curl_infotype type, char buf[TRC_LINE_MAX]; size_t len; if(data->set.fdebug) { - uint8_t inCallback = Curl_is_in_callback(data); - + struct Curl_mapi_guard guard; if(CURL_TRC_IDS(data) && (size < TRC_LINE_MAX)) { len = trc_print_ids(data, buf, TRC_LINE_MAX); len += curl_msnprintf(buf + len, TRC_LINE_MAX - len, "%.*s", (int)size, ptr); len = trc_end_buf(buf, len, TRC_LINE_MAX, FALSE); - Curl_set_in_callback(data, TRUE); + CURL_CBAPI_START(&guard, data, easy_fdebug); (void)(*data->set.fdebug)(data, type, buf, len, data->set.debugdata); - Curl_set_in_callback(data, inCallback); + CURL_CBAPI_END(&guard); } else { - Curl_set_in_callback(data, TRUE); + CURL_CBAPI_START(&guard, data, easy_fdebug); (void)(*data->set.fdebug)(data, type, CURL_UNCONST(ptr), size, data->set.debugdata); - Curl_set_in_callback(data, inCallback); + CURL_CBAPI_END(&guard); } } else { diff --git a/lib/cw-out.c b/lib/cw-out.c index 35ded4b475..5ff4a695a1 100644 --- a/lib/cw-out.c +++ b/lib/cw-out.c @@ -184,9 +184,12 @@ static CURLcode cw_out_cb_write(struct Curl_easy *data, DEBUGASSERT(data->conn); *pnwritten = 0; - Curl_set_in_callback(data, TRUE); - nwritten = wcb((char *)CURL_UNCONST(buf), 1, blen, wcb_data); - Curl_set_in_callback(data, FALSE); + { + struct Curl_mapi_guard guard; + CURL_CBAPI_START(&guard, data, easy_cw_out_cb); + nwritten = wcb((char *)CURL_UNCONST(buf), 1, blen, wcb_data); + CURL_CBAPI_END(&guard); + } CURL_TRC_WRITE(data, "[OUT] wrote %zu %s bytes -> %zu", blen, (otype == CW_OUT_HDS) ? "header" : "body", nwritten); diff --git a/lib/doh.c b/lib/doh.c index f336eb5003..776a96fea8 100644 --- a/lib/doh.c +++ b/lib/doh.c @@ -435,7 +435,7 @@ static CURLcode doh_probe_run(struct Curl_easy *data, private_data via CURLOPT_PRIVATE if they so choose. */ DEBUGASSERT(!doh->set.private_data); - if(curl_multi_add_handle(multi, doh)) + if(Curl_multi_add_handle(multi, doh)) goto error; *pmid = doh->mid; @@ -1339,7 +1339,7 @@ static void doh_close(struct Curl_easy *data, continue; } /* data->multi might already be reset at this time */ - curl_multi_remove_handle(data->multi, probe_data); + Curl_multi_remove_handle(data->multi, probe_data); Curl_close(&probe_data); } data->sub_xfer_done = NULL; diff --git a/lib/easy.c b/lib/easy.c index 2e2c4d0e25..fa67c90656 100644 --- a/lib/easy.c +++ b/lib/easy.c @@ -44,6 +44,7 @@ #endif #include "urldata.h" +#include "api.h" #include "transfer.h" #include "vtls/vtls.h" #include "vtls/vtls_scache.h" @@ -776,7 +777,7 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events) return CURLE_OUT_OF_MEMORY; } - if(multi->in_callback) + if(Curl_api_multi_is_in_callback(multi)) return CURLE_RECURSIVE_API_CALL; /* Copy relevant easy options to the multi handle */ @@ -784,7 +785,7 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events) curl_multi_setopt(multi, CURLMOPT_QUICK_EXIT, (long)data->set.quick_exit); data->multi_easy = NULL; /* pretend it does not exist */ - mresult = curl_multi_add_handle(multi, data); + mresult = Curl_multi_add_handle(multi, data); if(mresult) { curl_multi_cleanup(multi); if(mresult == CURLM_OUT_OF_MEMORY) @@ -803,7 +804,7 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events) /* 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); + (void)Curl_multi_remove_handle(multi, data); sigpipe_restore(&sigpipe_ctx); @@ -817,7 +818,14 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events) */ CURLcode curl_easy_perform(CURL *curl) { - return easy_perform(curl, FALSE); + struct Curl_eapi_guard guard; + CURLcode result; + + if(CURL_EAPI_ENTER(&guard, curl, easy_perform, &result)) { + result = easy_perform(curl, FALSE); + } + CURL_EAPI_LEAVE(&guard); + return result; } #ifdef DEBUGBUILD @@ -827,7 +835,14 @@ CURLcode curl_easy_perform(CURL *curl) */ CURLcode curl_easy_perform_ev(struct Curl_easy *easy) { - return easy_perform(easy, TRUE); + struct Curl_eapi_guard guard; + CURLcode result; + + if(CURL_EAPI_ENTER(&guard, easy, easy_perform_ev, &result)) { + result = easy_perform(easy, TRUE); + } + CURL_EAPI_LEAVE(&guard); + return result; } #endif @@ -837,13 +852,16 @@ CURLcode curl_easy_perform_ev(struct Curl_easy *easy) */ void curl_easy_cleanup(CURL *curl) { - struct Curl_easy *data = curl; - if(GOOD_EASY_HANDLE(data)) { + struct Curl_eapi_guard guard; + + if(CURL_EAPI_ENTER(&guard, curl, easy_cleanup, NULL)) { + struct Curl_easy *data = curl; struct Curl_sigpipe_ctx sigpipe_ctx; sigpipe_ignore(data, &sigpipe_ctx); Curl_close(&data); sigpipe_restore(&sigpipe_ctx); } + CURL_EAPI_LEAVE(&guard); } /* @@ -853,20 +871,22 @@ void curl_easy_cleanup(CURL *curl) #undef curl_easy_getinfo CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...) { - struct Curl_easy *data = curl; - va_list arg; - void *paramp; + struct Curl_eapi_guard guard; CURLcode result; - if(!GOOD_EASY_HANDLE(data)) - return CURLE_BAD_FUNCTION_ARGUMENT; + if(CURL_EAPI_ENTER(&guard, curl, easy_getinfo, &result)) { + struct Curl_easy *data = curl; + va_list arg; + void *paramp; - va_start(arg, info); - paramp = va_arg(arg, void *); + va_start(arg, info); + paramp = va_arg(arg, void *); - result = Curl_getinfo(data, info, paramp); + result = Curl_getinfo(data, info, paramp); - va_end(arg); + va_end(arg); + } + CURL_EAPI_LEAVE(&guard); return result; } @@ -952,122 +972,123 @@ static void dupeasy_meta_freeentry(void *p) */ CURL *curl_easy_duphandle(CURL *curl) { - struct Curl_easy *data = curl; + struct Curl_eapi_guard guard; struct Curl_easy *outcurl = NULL; - if(!GOOD_EASY_HANDLE(data)) - goto fail; - outcurl = curlx_calloc(1, sizeof(struct Curl_easy)); - if(!outcurl) - goto fail; + if(CURL_EAPI_ENTER(&guard, curl, easy_duphandle, NULL)) { + struct Curl_easy *data = curl; - /* - * 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; + outcurl = curlx_calloc(1, sizeof(struct Curl_easy)); + if(!outcurl) + goto fail; - Curl_hash_init(&outcurl->meta_hash, 23, - Curl_hash_str, curlx_str_key_compare, dupeasy_meta_freeentry); - curlx_dyn_init(&outcurl->state.headerb, CURL_MAX_HTTP_HEADER); - Curl_bufref_init(&outcurl->state.url); - Curl_bufref_init(&outcurl->state.referer); - Curl_netrc_init(&outcurl->state.netrc); + /* + * 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; - /* the connection pool is setup on demand */ - outcurl->state.lastconnect_id = -1; - outcurl->state.recent_conn_id = -1; - outcurl->id = -1; - outcurl->mid = UINT32_MAX; - outcurl->master_mid = UINT32_MAX; + Curl_hash_init(&outcurl->meta_hash, 23, + Curl_hash_str, curlx_str_key_compare, + dupeasy_meta_freeentry); + curlx_dyn_init(&outcurl->state.headerb, CURL_MAX_HTTP_HEADER); + Curl_bufref_init(&outcurl->state.url); + Curl_bufref_init(&outcurl->state.referer); + Curl_netrc_init(&outcurl->state.netrc); + + /* the connection pool is setup on demand */ + outcurl->state.lastconnect_id = -1; + outcurl->state.recent_conn_id = -1; + outcurl->id = -1; + outcurl->mid = UINT32_MAX; + outcurl->master_mid = UINT32_MAX; #ifndef CURL_DISABLE_HTTP - Curl_llist_init(&outcurl->state.httphdrs, NULL); + Curl_llist_init(&outcurl->state.httphdrs, NULL); #endif - Curl_initinfo(outcurl); + Curl_initinfo(outcurl); - /* copy all userdefined values */ - if(dupset(outcurl, data)) - goto fail; + /* copy all userdefined values */ + if(dupset(outcurl, data)) + goto fail; - outcurl->progress.hide = data->progress.hide; - outcurl->progress.callback = data->progress.callback; + outcurl->progress.hide = data->progress.hide; + 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(); - if(!outcurl->cookies) - goto fail; - outcurl->state.cookie_engine = TRUE; - } + 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(); + if(!outcurl->cookies) + goto fail; + outcurl->state.cookie_engine = TRUE; + } - if(data->state.cookielist) { - outcurl->state.cookielist = Curl_slist_duplicate(data->state.cookielist); - if(!outcurl->state.cookielist) - goto fail; - } + if(data->state.cookielist) { + outcurl->state.cookielist = Curl_slist_duplicate(data->state.cookielist); + if(!outcurl->state.cookielist) + goto fail; + } #endif - if(Curl_bufref_ptr(&data->state.url)) { - Curl_bufref_set(&outcurl->state.url, - Curl_bufref_dup(&data->state.url), 0, - curl_free); - if(!Curl_bufref_ptr(&outcurl->state.url)) - goto fail; - } - if(Curl_bufref_ptr(&data->state.referer)) { - Curl_bufref_set(&outcurl->state.referer, - Curl_bufref_dup(&data->state.referer), 0, - curl_free); - if(!Curl_bufref_ptr(&outcurl->state.referer)) - goto fail; - } + if(Curl_bufref_ptr(&data->state.url)) { + Curl_bufref_set(&outcurl->state.url, + Curl_bufref_dup(&data->state.url), 0, + curl_free); + if(!Curl_bufref_ptr(&outcurl->state.url)) + goto fail; + } + if(Curl_bufref_ptr(&data->state.referer)) { + Curl_bufref_set(&outcurl->state.referer, + Curl_bufref_dup(&data->state.referer), 0, + curl_free); + if(!Curl_bufref_ptr(&outcurl->state.referer)) + goto fail; + } - /* 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; - } + /* 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]); - } + 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); + 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); - /* Copy entries learned at runtime. (E.g. Strict-Transport-Security - headers.) */ - if(Curl_hsts_copy(outcurl->hsts, data->hsts)) - goto fail; - } + /* Copy entries learned at runtime. (E.g. Strict-Transport-Security + headers.) */ + if(Curl_hsts_copy(outcurl->hsts, data->hsts)) + goto fail; + } #endif - outcurl->magic = CURLEASY_MAGIC_NUMBER; - - /* we reach this point and thus we are OK */ - + /* we reach this point and thus we are OK */ + outcurl->magic = CURLEASY_MAGIC_NUMBER; + } + CURL_EAPI_LEAVE(&guard); return outcurl; fail: - if(outcurl) { #ifndef CURL_DISABLE_COOKIES curlx_free(outcurl->cookies); @@ -1079,6 +1100,7 @@ fail: curlx_free(outcurl); } + CURL_EAPI_LEAVE(&guard); return NULL; } @@ -1088,38 +1110,41 @@ fail: */ void curl_easy_reset(CURL *curl) { - struct Curl_easy *data = curl; - if(!GOOD_EASY_HANDLE(data)) - return; + struct Curl_eapi_guard guard; - Curl_req_hard_reset(&data->req, data); - Curl_hash_clean(&data->meta_hash); + if(CURL_EAPI_ENTER(&guard, curl, easy_reset, NULL)) { + struct Curl_easy *data = curl; - /* clear all meta data */ - Curl_meta_reset(data); - /* zero out UserDefined data: */ - Curl_freeset(data); - memset(&data->set, 0, sizeof(struct UserDefined)); - Curl_init_userdefined(data); + Curl_req_hard_reset(&data->req, data); + Curl_hash_clean(&data->meta_hash); - /* zero out Progress data: */ - memset(&data->progress, 0, sizeof(struct Progress)); + /* clear all meta data */ + Curl_meta_reset(data); + /* zero out UserDefined data: */ + Curl_freeset(data); + memset(&data->set, 0, sizeof(struct UserDefined)); + Curl_init_userdefined(data); - /* zero out PureInfo data: */ - Curl_initinfo(data); + /* zero out Progress data: */ + memset(&data->progress, 0, sizeof(struct Progress)); - data->progress.hide = TRUE; - data->state.current_speed = -1; /* init to negative == impossible */ - data->state.recent_conn_id = -1; /* clear remembered connection id */ + /* zero out PureInfo data: */ + Curl_initinfo(data); - /* zero out authentication data: */ - memset(&data->state.authhost, 0, sizeof(struct auth)); - memset(&data->state.authproxy, 0, sizeof(struct auth)); + data->progress.hide = TRUE; + data->state.current_speed = -1; /* init to negative == impossible */ + data->state.recent_conn_id = -1; /* clear remembered connection id */ + + /* 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); + Curl_http_auth_cleanup_digest(data); #endif - data->master_mid = UINT32_MAX; + data->master_mid = UINT32_MAX; + } + CURL_EAPI_LEAVE(&guard); } /* @@ -1137,63 +1162,60 @@ void curl_easy_reset(CURL *curl) */ CURLcode curl_easy_pause(CURL *curl, int action) { + struct Curl_eapi_guard guard; CURLcode result = CURLE_OK; - bool changed = FALSE; - struct Curl_easy *data = curl; - bool recv_paused, recv_paused_new; - bool send_paused, send_paused_new; - uint8_t in_c; - if(!GOOD_EASY_HANDLE(data) || !data->conn) - /* crazy input, do not continue */ - return CURLE_BAD_FUNCTION_ARGUMENT; + if(CURL_EAPI_ENTER(&guard, curl, easy_pause, &result)) { + bool changed = FALSE; + struct Curl_easy *data = curl; + bool recv_paused, recv_paused_new; + bool send_paused, send_paused_new; - in_c = Curl_is_in_callback(data); - if(in_c == IN_CALLBACK_FORBID_EASY_PAUSE) - return CURLE_RECURSIVE_API_CALL; + if(!data->conn) { + /* crazy input, do not continue */ + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; + } - recv_paused = Curl_xfer_recv_is_paused(data); - recv_paused_new = (action & CURLPAUSE_RECV); - send_paused = Curl_xfer_send_is_paused(data); - send_paused_new = (action & CURLPAUSE_SEND); + recv_paused = Curl_xfer_recv_is_paused(data); + recv_paused_new = (action & CURLPAUSE_RECV); + send_paused = Curl_xfer_send_is_paused(data); + send_paused_new = (action & CURLPAUSE_SEND); - if((send_paused != send_paused_new) || - (send_paused_new != Curl_creader_is_paused(data))) { - changed = TRUE; - result = Curl_1st_fatal( - result, Curl_xfer_pause_send(data, send_paused_new)); - } + if((send_paused != send_paused_new) || + (send_paused_new != Curl_creader_is_paused(data))) { + changed = TRUE; + result = Curl_1st_fatal( + result, Curl_xfer_pause_send(data, send_paused_new)); + } - if(recv_paused != recv_paused_new) { - changed = TRUE; - result = Curl_1st_fatal( - result, Curl_xfer_pause_recv(data, recv_paused_new)); - } + if(recv_paused != recv_paused_new) { + changed = TRUE; + result = Curl_1st_fatal( + result, Curl_xfer_pause_recv(data, recv_paused_new)); + } - /* If not completely pausing both directions now, run again in any case. */ - if(!Curl_xfer_is_blocked(data)) { - /* reset the too-slow time keeper */ - data->state.keeps_speed.tv_sec = 0; - if(data->multi) { - Curl_multi_mark_dirty(data); /* make it run */ - /* On changes, tell application to update its timers. */ - if(changed) { - if(Curl_update_timer(data->multi) && !result) - result = CURLE_ABORTED_BY_CALLBACK; + /* If not completely pausing both directions, run again in any case. */ + if(!Curl_xfer_is_blocked(data)) { + /* reset the too-slow time keeper */ + data->state.keeps_speed.tv_sec = 0; + if(data->multi) { + Curl_multi_mark_dirty(data); /* make it run */ + /* On changes, tell application to update its timers. */ + if(changed) { + if(Curl_update_timer(data->multi) && !result) + result = CURLE_ABORTED_BY_CALLBACK; + } } } + + if(!result && changed && !data->state.done && data->multi) + /* pause/unpausing may result in multi event changes */ + if(Curl_multi_ev_assess_xfer(data->multi, data) && !result) + result = CURLE_ABORTED_BY_CALLBACK; } - - if(!result && changed && !data->state.done && data->multi) - /* pause/unpausing may result in multi event changes */ - if(Curl_multi_ev_assess_xfer(data->multi, data) && !result) - result = CURLE_ABORTED_BY_CALLBACK; - - if(in_c) - /* this might have called a callback recursively which might have set this - to false again on exit */ - Curl_set_in_callback(data, in_c); - +out: + CURL_EAPI_LEAVE(&guard); return result; } @@ -1226,16 +1248,11 @@ static CURLcode easy_connection(struct Curl_easy *data, * curl_easy_perform() with CURLOPT_CONNECT_ONLY option. * Returns CURLE_OK on success, error code on error. */ -CURLcode curl_easy_recv(CURL *curl, void *buffer, size_t buflen, size_t *n) +CURLcode Curl_easy_recv(struct Curl_easy *data, + void *buffer, size_t buflen, size_t *n) { CURLcode result; struct connectdata *c; - struct Curl_easy *data = curl; - - if(!GOOD_EASY_HANDLE(data)) - return CURLE_BAD_FUNCTION_ARGUMENT; - if(Curl_is_in_callback(data)) - return CURLE_RECURSIVE_API_CALL; result = easy_connection(data, &c); if(result) @@ -1250,6 +1267,18 @@ CURLcode curl_easy_recv(CURL *curl, void *buffer, size_t buflen, size_t *n) return Curl_conn_recv(data, FIRSTSOCKET, buffer, buflen, n); } +CURLcode curl_easy_recv(CURL *curl, void *buffer, size_t buflen, size_t *n) +{ + struct Curl_eapi_guard guard; + CURLcode result; + + if(CURL_EAPI_ENTER(&guard, curl, easy_recv, &result)) { + result = Curl_easy_recv(curl, buffer, buflen, n); + } + CURL_EAPI_LEAVE(&guard); + return result; +} + #ifndef CURL_DISABLE_WEBSOCKETS CURLcode Curl_connect_only_attach(struct Curl_easy *data) { @@ -1307,16 +1336,17 @@ CURLcode Curl_senddata(struct Curl_easy *data, const void *buffer, CURLcode curl_easy_send(CURL *curl, const void *buffer, size_t buflen, size_t *n) { - size_t written = 0; + struct Curl_eapi_guard guard; CURLcode result; - struct Curl_easy *data = curl; - if(!GOOD_EASY_HANDLE(data)) - return CURLE_BAD_FUNCTION_ARGUMENT; - if(Curl_is_in_callback(data)) - return CURLE_RECURSIVE_API_CALL; - result = Curl_senddata(data, buffer, buflen, &written); - *n = written; + if(CURL_EAPI_ENTER(&guard, curl, easy_send, &result)) { + struct Curl_easy *data = curl; + size_t written = 0; + + result = Curl_senddata(data, buffer, buflen, &written); + *n = written; + } + CURL_EAPI_LEAVE(&guard); return result; } @@ -1325,16 +1355,15 @@ CURLcode curl_easy_send(CURL *curl, const void *buffer, size_t buflen, */ CURLcode curl_easy_upkeep(CURL *curl) { - struct Curl_easy *data = curl; - /* Verify that we got an easy handle we can work with. */ - if(!GOOD_EASY_HANDLE(data)) - return CURLE_BAD_FUNCTION_ARGUMENT; + struct Curl_eapi_guard guard; + CURLcode result; - if(Curl_is_in_callback(data)) - return CURLE_RECURSIVE_API_CALL; - - /* Use the common function to keep connections alive. */ - return Curl_cpool_upkeep(data); + if(CURL_EAPI_ENTER(&guard, curl, easy_upkeep, &result)) { + /* Use the common function to keep connections alive. */ + result = Curl_cpool_upkeep((struct Curl_easy *)curl); + } + CURL_EAPI_LEAVE(&guard); + return result; } CURLcode curl_easy_ssls_import(CURL *curl, const char *session_key, @@ -1342,13 +1371,15 @@ CURLcode curl_easy_ssls_import(CURL *curl, const char *session_key, const unsigned char *sdata, size_t sdata_len) { #if defined(USE_SSL) && defined(USE_SSLS_EXPORT) - struct Curl_easy *data = curl; - if(!GOOD_EASY_HANDLE(data)) - return CURLE_BAD_FUNCTION_ARGUMENT; - if(Curl_is_in_callback(data) || Curl_ssl_scache_is_locked(data)) - return CURLE_RECURSIVE_API_CALL; - return Curl_ssl_session_import(data, session_key, - shmac, shmac_len, sdata, sdata_len); + struct Curl_eapi_guard guard; + CURLcode result; + + if(CURL_EAPI_ENTER(&guard, curl, easy_ssls_import, &result)) { + result = Curl_ssl_session_import((struct Curl_easy *)curl, session_key, + shmac, shmac_len, sdata, sdata_len); + } + CURL_EAPI_LEAVE(&guard); + return result; #else (void)curl; (void)session_key; @@ -1365,12 +1396,15 @@ CURLcode curl_easy_ssls_export(CURL *curl, void *userptr) { #if defined(USE_SSL) && defined(USE_SSLS_EXPORT) - struct Curl_easy *data = curl; - if(!GOOD_EASY_HANDLE(data)) - return CURLE_BAD_FUNCTION_ARGUMENT; - if(Curl_is_in_callback(data) || Curl_ssl_scache_is_locked(data)) - return CURLE_RECURSIVE_API_CALL; - return Curl_ssl_session_export(data, export_fn, userptr); + struct Curl_eapi_guard guard; + CURLcode result; + + if(CURL_EAPI_ENTER(&guard, curl, easy_ssls_export, &result)) { + result = Curl_ssl_session_export((struct Curl_easy *)curl, + export_fn, userptr); + } + CURL_EAPI_LEAVE(&guard); + return result; #else (void)curl; (void)export_fn; diff --git a/lib/ftp.c b/lib/ftp.c index f3c07d6722..338a7280b0 100644 --- a/lib/ftp.c +++ b/lib/ftp.c @@ -1714,10 +1714,11 @@ static CURLcode ftp_state_ul_setup(struct Curl_easy *data, /* Let's read off the proper amount of bytes from the input. */ if(data->set.seek_func) { - Curl_set_in_callback(data, TRUE); + struct Curl_mapi_guard guard; + CURL_CBAPI_START(&guard, data, easy_seek_func); seekerr = data->set.seek_func(data->set.seek_client, data->state.resume_from, SEEK_SET); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); } if(seekerr != CURL_SEEKFUNC_OK) { @@ -3658,9 +3659,10 @@ static void ftp_done_wildcard(struct Curl_easy *data, struct ftp_conn *ftpc) { if(data->state.wildcardmatch) { if(data->set.chunk_end && ftpc->file) { - Curl_set_in_callback(data, TRUE); + struct Curl_mapi_guard guard; + CURL_CBAPI_START(&guard, data, easy_chunk_end); data->set.chunk_end(data->set.wildcardptr); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); freedirs(ftpc); } ftpc->known_filesize = -1; @@ -4101,11 +4103,12 @@ static CURLcode wc_statemach(struct Curl_easy *data, infof(data, "Wildcard - START of \"%s\"", finfo->filename); if(data->set.chunk_bgn) { long userresponse; - Curl_set_in_callback(data, TRUE); + struct Curl_mapi_guard guard; + CURL_CBAPI_START(&guard, data, easy_chunk_bgn); userresponse = data->set.chunk_bgn( finfo, data->set.wildcardptr, (int)Curl_llist_count(&wildcard->filelist)); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); switch(userresponse) { case CURL_CHUNK_BGN_FUNC_SKIP: infof(data, "Wildcard - \"%s\" skipped by user", finfo->filename); @@ -4143,9 +4146,10 @@ static CURLcode wc_statemach(struct Curl_easy *data, case CURLWC_SKIP: { if(data->set.chunk_end) { - Curl_set_in_callback(data, TRUE); + struct Curl_mapi_guard guard; + CURL_CBAPI_START(&guard, data, easy_chunk_end); data->set.chunk_end(data->set.wildcardptr); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); } Curl_node_remove(Curl_llist_head(&wildcard->filelist)); wildcard->state = (Curl_llist_count(&wildcard->filelist) == 0) ? diff --git a/lib/ftplistparser.c b/lib/ftplistparser.c index 23fbd1f07c..2fbff9ea38 100644 --- a/lib/ftplistparser.c +++ b/lib/ftplistparser.c @@ -323,18 +323,21 @@ static CURLcode ftp_pl_insert_finfo(struct Curl_easy *data, compare = Curl_fnmatch; /* filter pattern-corresponding filenames */ - Curl_set_in_callback(data, TRUE); - if(compare(data->set.fnmatch_data, wc->pattern, finfo->filename) == 0) { - /* discard symlink which is containing multiple " -> " */ - if((finfo->filetype == CURLFILETYPE_SYMLINK) && finfo->strings.target && - (strstr(finfo->strings.target, " -> "))) { + { + struct Curl_mapi_guard guard; + CURL_CBAPI_START(&guard, data, easy_fnmatch_data); + if(compare(data->set.fnmatch_data, wc->pattern, finfo->filename) == 0) { + /* discard symlink which is containing multiple " -> " */ + if((finfo->filetype == CURLFILETYPE_SYMLINK) && finfo->strings.target && + (strstr(finfo->strings.target, " -> "))) { + add = FALSE; + } + } + else { add = FALSE; } + CURL_CBAPI_END(&guard); } - else { - add = FALSE; - } - Curl_set_in_callback(data, FALSE); if(add) { Curl_llist_append(llist, finfo, &infop->list); diff --git a/lib/hostip.c b/lib/hostip.c index dc39f581b2..7427f8fdd9 100644 --- a/lib/hostip.c +++ b/lib/hostip.c @@ -363,13 +363,14 @@ CURLcode Curl_resolv_announce_start(struct Curl_easy *data, void *resolver) { if(data->set.resolver_start) { + struct Curl_mapi_guard guard; int rc; CURL_TRC_DNS(data, "announcing resolve to application"); - Curl_set_in_callback(data, TRUE); + CURL_CBAPI_START(&guard, data, easy_resolver_start); rc = data->set.resolver_start(resolver, NULL, data->set.resolver_start_client); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); if(rc) { CURL_TRC_DNS(data, "application aborted resolve"); return CURLE_ABORTED_BY_CALLBACK; diff --git a/lib/http2.c b/lib/http2.c index ff3d3d2741..8612efc302 100644 --- a/lib/http2.c +++ b/lib/http2.c @@ -817,11 +817,14 @@ static int push_promise(struct Curl_cfilter *cf, goto fail; } - Curl_set_in_callback(data, TRUE); - rv = data->multi->push_cb(data, newhandle, - stream->push_headers_used, &heads, - data->multi->push_userp); - Curl_set_in_callback(data, FALSE); + { + struct Curl_mapi_guard guard; + CURL_CBAPI_START(&guard, data, multi_push_cb); + rv = data->multi->push_cb(data, newhandle, + stream->push_headers_used, &heads, + data->multi->push_userp); + CURL_CBAPI_END(&guard); + } /* free the headers again */ free_push_headers(stream); diff --git a/lib/http_chunks.c b/lib/http_chunks.c index 93f5bb6c42..fe8e98bd17 100644 --- a/lib/http_chunks.c +++ b/lib/http_chunks.c @@ -519,9 +519,12 @@ static CURLcode add_last_chunk(struct Curl_easy *data, if(result) goto out; - Curl_set_in_callback(data, TRUE); - rc = data->set.trailer_callback(&trailers, data->set.trailer_data); - Curl_set_in_callback(data, FALSE); + { + struct Curl_mapi_guard guard; + CURL_CBAPI_START(&guard, data, easy_trailer_callback); + rc = data->set.trailer_callback(&trailers, data->set.trailer_data); + CURL_CBAPI_END(&guard); + } if(rc != CURL_TRAILERFUNC_OK) { failf(data, "operation aborted by trailing headers callback"); diff --git a/lib/multi.c b/lib/multi.c index 9e3b092e48..c18d30be19 100644 --- a/lib/multi.c +++ b/lib/multi.c @@ -72,20 +72,6 @@ #define CURL_TLS_SESSION_SIZE 25 #endif -#define CURL_MULTI_HANDLE 0x000bab1e - -#ifdef DEBUGBUILD -/* On a debug build, we want to fail hard on multi handles that - * are not NULL, but no longer have the MAGIC touch. This gives - * us early warning on things only discovered by valgrind otherwise. */ -#define GOOD_MULTI_HANDLE(x) \ - (((x) && (x)->magic == CURL_MULTI_HANDLE) ? TRUE : \ - (DEBUGASSERT(!(x)), FALSE)) -#else -#define GOOD_MULTI_HANDLE(x) \ - ((x) && (x)->magic == CURL_MULTI_HANDLE) -#endif - static void move_pending_to_connect(struct Curl_multi *multi, struct Curl_easy *data); static CURLMcode add_next_timeout(const struct curltime *pnow, @@ -247,7 +233,7 @@ struct Curl_multi *Curl_multi_handle(uint32_t xfer_table_size, if(!multi) return NULL; - multi->magic = CURL_MULTI_HANDLE; + multi->magic = CURLMULTI_MAGIC_NUMBER; Curl_dnscache_init(&multi->dnscache, dnssize); Curl_mntfy_init(multi); @@ -474,28 +460,16 @@ static CURLMcode multi_xfers_add(struct Curl_multi *multi, return CURLM_OK; } -CURLMcode curl_multi_add_handle(CURLM *m, CURL *curl) +CURLMcode Curl_multi_add_handle(struct Curl_multi *multi, + struct Curl_easy *data) { CURLMcode mresult; - struct Curl_multi *multi = m; - struct Curl_easy *data = curl; - - /* First, make some basic checks that the CURLM handle is a good handle */ - if(!GOOD_MULTI_HANDLE(multi)) - return CURLM_BAD_HANDLE; - - /* Verify that we got a somewhat good easy handle too */ - if(!GOOD_EASY_HANDLE(data)) - return CURLM_BAD_EASY_HANDLE; /* Prevent users from adding same easy handle more than once and prevent adding to more than one multi stack */ if(data->multi) return CURLM_ADDED_ALREADY; - if(multi->in_callback) - return CURLM_RECURSIVE_API_CALL; - if(multi->dead) { /* a "dead" handle cannot get added transfers while any existing easy handles are still alive - but if there are none alive anymore, it is @@ -526,18 +500,18 @@ CURLMcode curl_multi_add_handle(CURLM *m, CURL *curl) Curl_llist_init(&data->state.timeoutlist, NULL); /* - * No failure allowed in this function beyond this point. No modification of - * easy nor multi handle allowed before this except for potential multi's - * connection pool growing which will not be undone in this function no - * matter what. + * No failure allowed in this function beyond this point. No modification + * of easy nor multi handle allowed before this except for potential + * multi's connection pool growing which will not be undone in this + * function no matter what. */ if(data->set.errorbuffer) data->set.errorbuffer[0] = 0; data->state.os_errno = 0; - /* make the Curl_easy refer back to this multi handle - before Curl_expire() - is called. */ + /* make the Curl_easy refer back to this multi handle - before + Curl_expire() is called. */ data->multi = multi; /* set the easy handle */ @@ -589,6 +563,23 @@ CURLMcode curl_multi_add_handle(CURLM *m, CURL *curl) return CURLM_OK; } +CURLMcode curl_multi_add_handle(CURLM *m, CURL *curl) +{ + struct Curl_mapi_guard guard; + CURLMcode mresult; + + if(CURL_MAPI_ENTER(&guard, m, multi_add_handle, &mresult)) { + struct Curl_easy *data = curl; + /* Verify that we got a somewhat good easy handle too */ + if(!GOOD_EASY_HANDLE(data)) + mresult = CURLM_BAD_EASY_HANDLE; + else + mresult = Curl_multi_add_handle(m, data); + } + CURL_MAPI_LEAVE(&guard); + return mresult; +} + #if 0 /* Debug-function, used like this: * @@ -792,23 +783,14 @@ static void close_connect_only(struct connectdata *conn, connclose(conn, "Removing connect-only easy handle"); } -CURLMcode curl_multi_remove_handle(CURLM *m, CURL *curl) +CURLMcode Curl_multi_remove_handle(struct Curl_multi *multi, + struct Curl_easy *data) { - struct Curl_multi *multi = m; - struct Curl_easy *data = curl; + CURLMcode mresult; bool premature; struct Curl_llist_node *e; - CURLMcode mresult; uint32_t mid; - /* First, make some basic checks that the CURLM handle is a good handle */ - if(!GOOD_MULTI_HANDLE(multi)) - return CURLM_BAD_HANDLE; - - /* Verify that we got a somewhat good easy handle too */ - if(!GOOD_EASY_HANDLE(data)) - return CURLM_BAD_EASY_HANDLE; - /* Prevent users from trying to remove same easy handle more than once */ if(!data->multi) return CURLM_OK; /* it is already removed so let's say it is fine! */ @@ -826,9 +808,6 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *curl) return CURLM_INTERNAL_ERROR; } - if(multi->in_callback) - return CURLM_RECURSIVE_API_CALL; - premature = (data->mstate < MSTATE_COMPLETED); /* If the 'state' is not INIT or COMPLETED, we might need to do something @@ -948,6 +927,22 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *curl) return CURLM_OK; } +CURLMcode curl_multi_remove_handle(CURLM *m, CURL *curl) +{ + struct Curl_mapi_guard guard; + CURLMcode mresult; + + if(CURL_MAPI_ENTER(&guard, m, multi_remove_handle, &mresult)) { + struct Curl_easy *data = curl; + if(!GOOD_EASY_HANDLE(data)) + mresult = CURLM_BAD_EASY_HANDLE; + else + mresult = Curl_multi_remove_handle(m, data); + } + CURL_MAPI_LEAVE(&guard); + return mresult; +} + /* Return TRUE if the application asked for multiplexing */ bool Curl_multiplex_wanted(const struct Curl_multi *multi) { @@ -1272,54 +1267,55 @@ CURLMcode curl_multi_fdset(CURLM *m, fd_set *read_fd_set, fd_set *write_fd_set, fd_set *exc_fd_set, int *max_fd) { - /* Scan through all the easy handles to get the file descriptors set. - Some easy handles may not have connected to the remote host yet, - and then we must make sure that is done. */ - int this_max_fd = -1; - struct Curl_multi *multi = m; - struct easy_pollset ps; - unsigned int i; - uint32_t mid; - (void)exc_fd_set; + struct Curl_mapi_guard guard; + CURLMcode mresult; - if(!GOOD_MULTI_HANDLE(multi)) - return CURLM_BAD_HANDLE; + if(CURL_MAPI_ENTER(&guard, m, multi_fdset, &mresult)) { + /* Scan through all the easy handles to get the file descriptors set. + Some easy handles may not have connected to the remote host yet, + and then we must make sure that is done. */ + struct Curl_multi *multi = m; + struct easy_pollset ps; + int this_max_fd = -1; + unsigned int i; + uint32_t mid; + (void)exc_fd_set; - if(multi->in_callback) - return CURLM_RECURSIVE_API_CALL; + Curl_pollset_init(&ps); + if(Curl_uint32_bset_first(&multi->process, &mid)) { + do { + struct Curl_easy *data = Curl_multi_get_easy(multi, mid); - Curl_pollset_init(&ps); - if(Curl_uint32_bset_first(&multi->process, &mid)) { - do { - struct Curl_easy *data = Curl_multi_get_easy(multi, mid); - - if(!data) { - DEBUGASSERT(0); - continue; - } - - Curl_multi_pollset(data, &ps); - for(i = 0; i < ps.n; i++) { - if(!FDSET_SOCK(ps.sockets[i])) - /* pretend it does not exist */ + if(!data) { + DEBUGASSERT(0); continue; - if(ps.actions[i] & CURL_POLL_IN) - FD_SET(ps.sockets[i], read_fd_set); - if(ps.actions[i] & CURL_POLL_OUT) - FD_SET(ps.sockets[i], write_fd_set); - if((int)ps.sockets[i] > this_max_fd) - this_max_fd = (int)ps.sockets[i]; - } - } while(Curl_uint32_bset_next(&multi->process, mid, &mid)); + } + + Curl_multi_pollset(data, &ps); + for(i = 0; i < ps.n; i++) { + if(!FDSET_SOCK(ps.sockets[i])) + /* pretend it does not exist */ + continue; + if(ps.actions[i] & CURL_POLL_IN) + FD_SET(ps.sockets[i], read_fd_set); + if(ps.actions[i] & CURL_POLL_OUT) + FD_SET(ps.sockets[i], write_fd_set); + if((int)ps.sockets[i] > this_max_fd) + this_max_fd = (int)ps.sockets[i]; + } + } while(Curl_uint32_bset_next(&multi->process, mid, &mid)); + } + + Curl_cshutdn_setfds(&multi->cshutdn, multi->admin, + read_fd_set, write_fd_set, &this_max_fd); + + *max_fd = this_max_fd; + Curl_pollset_cleanup(&ps); + + mresult = CURLM_OK; } - - Curl_cshutdn_setfds(&multi->cshutdn, multi->admin, - read_fd_set, write_fd_set, &this_max_fd); - - *max_fd = this_max_fd; - Curl_pollset_cleanup(&ps); - - return CURLM_OK; + CURL_MAPI_LEAVE(&guard); + return mresult; } CURLMcode curl_multi_waitfds(CURLM *m, @@ -1327,46 +1323,49 @@ CURLMcode curl_multi_waitfds(CURLM *m, unsigned int size, unsigned int *fd_count) { - struct Curl_waitfds cwfds; - CURLMcode mresult = CURLM_OK; - struct Curl_multi *multi = m; - struct easy_pollset ps; - unsigned int need = 0; - uint32_t mid; + struct Curl_mapi_guard guard; + CURLMcode mresult; - if(!ufds && (size || !fd_count)) - return CURLM_BAD_FUNCTION_ARGUMENT; + if(CURL_MAPI_ENTER(&guard, m, multi_waitfds, &mresult)) { + struct Curl_waitfds cwfds; + struct Curl_multi *multi = m; + struct easy_pollset ps; + unsigned int need = 0; + uint32_t mid; - if(!GOOD_MULTI_HANDLE(multi)) - return CURLM_BAD_HANDLE; + if(!ufds && (size || !fd_count)) { + mresult = CURLM_BAD_FUNCTION_ARGUMENT; + goto out; + } - if(multi->in_callback) - return CURLM_RECURSIVE_API_CALL; + Curl_pollset_init(&ps); + Curl_waitfds_init(&cwfds, ufds, size); + mresult = CURLM_OK; + if(Curl_uint32_bset_first(&multi->process, &mid)) { + do { + struct Curl_easy *data = Curl_multi_get_easy(multi, mid); + if(!data) { + DEBUGASSERT(0); + Curl_uint32_bset_remove(&multi->process, mid); + Curl_uint32_bset_remove(&multi->dirty, mid); + continue; + } + Curl_multi_pollset(data, &ps); + need += Curl_waitfds_add_ps(&cwfds, &ps); + } while(Curl_uint32_bset_next(&multi->process, mid, &mid)); + } - Curl_pollset_init(&ps); - Curl_waitfds_init(&cwfds, ufds, size); - if(Curl_uint32_bset_first(&multi->process, &mid)) { - do { - struct Curl_easy *data = Curl_multi_get_easy(multi, mid); - if(!data) { - DEBUGASSERT(0); - Curl_uint32_bset_remove(&multi->process, mid); - Curl_uint32_bset_remove(&multi->dirty, mid); - continue; - } - Curl_multi_pollset(data, &ps); - need += Curl_waitfds_add_ps(&cwfds, &ps); - } while(Curl_uint32_bset_next(&multi->process, mid, &mid)); + need += Curl_cshutdn_add_waitfds(&multi->cshutdn, multi->admin, &cwfds); + + if(need != cwfds.n && ufds) + mresult = CURLM_OUT_OF_MEMORY; + + if(fd_count) + *fd_count = need; + Curl_pollset_cleanup(&ps); } - - need += Curl_cshutdn_add_waitfds(&multi->cshutdn, multi->admin, &cwfds); - - if(need != cwfds.n && ufds) - mresult = CURLM_OUT_OF_MEMORY; - - if(fd_count) - *fd_count = need; - Curl_pollset_cleanup(&ps); +out: + CURL_MAPI_LEAVE(&guard); return mresult; } @@ -1570,12 +1569,6 @@ static CURLMcode multi_wait(struct Curl_multi *multi, int wakeup_idx = -1; #endif - if(!GOOD_MULTI_HANDLE(multi)) - return CURLM_BAD_HANDLE; - - if(multi->in_callback) - return CURLM_RECURSIVE_API_CALL; - if(timeout_ms < 0) return CURLM_BAD_FUNCTION_ARGUMENT; @@ -1683,7 +1676,14 @@ CURLMcode curl_multi_wait(CURLM *m, int timeout_ms, int *ret) { - return multi_wait(m, extra_fds, extra_nfds, timeout_ms, ret, FALSE); + struct Curl_mapi_guard guard; + CURLMcode mresult; + + if(CURL_MAPI_ENTER(&guard, m, multi_wait, &mresult)) { + mresult = multi_wait(m, extra_fds, extra_nfds, timeout_ms, ret, FALSE); + } + CURL_MAPI_LEAVE(&guard); + return mresult; } CURLMcode curl_multi_poll(CURLM *m, @@ -1692,7 +1692,14 @@ CURLMcode curl_multi_poll(CURLM *m, int timeout_ms, int *ret) { - return multi_wait(m, extra_fds, extra_nfds, timeout_ms, ret, TRUE); + struct Curl_mapi_guard guard; + CURLMcode mresult; + + if(CURL_MAPI_ENTER(&guard, m, multi_poll, &mresult)) { + mresult = multi_wait(m, extra_fds, extra_nfds, timeout_ms, ret, TRUE); + } + CURL_MAPI_LEAVE(&guard); + return mresult; } CURLMcode curl_multi_wakeup(CURLM *m) @@ -1771,10 +1778,7 @@ CURLMcode Curl_multi_add_perform(struct Curl_multi *multi, { CURLMcode mresult; - if(multi->in_callback) - return CURLM_RECURSIVE_API_CALL; - - mresult = curl_multi_add_handle(multi, data); + mresult = Curl_multi_add_handle(multi, data); if(!mresult) { CURLcode result; @@ -1782,7 +1786,7 @@ CURLMcode Curl_multi_add_perform(struct Curl_multi *multi, connection, only this transfer */ result = Curl_init_do(data, NULL); if(result) { - curl_multi_remove_handle(multi, data); + Curl_multi_remove_handle(multi, data); return CURLM_INTERNAL_ERROR; } @@ -1955,11 +1959,6 @@ static CURLcode protocol_connect(struct Curl_easy *data, bool *protocol_done) return CURLE_OK; } -static void set_in_callback(struct Curl_multi *multi, bool value) -{ - multi->in_callback = value; -} - /* * posttransfer() is called immediately after a transfer ends */ @@ -2186,16 +2185,17 @@ static CURLMcode multistate_do(struct Curl_easy *data, CURLMcode mresult = CURLM_OK; CURLcode result = CURLE_OK; if(data->set.fprereq) { + struct Curl_mapi_guard guard; int prereq_rc; /* call the prerequest callback function */ - Curl_set_in_callback(data, TRUE); + CURL_CBAPI_START(&guard, data, easy_fprereq); prereq_rc = data->set.fprereq(data->set.prereq_userp, data->info.primary.remote_ip, data->info.primary.local_ip, data->info.primary.remote_port, data->info.primary.local_port); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); if(prereq_rc != CURL_PREREQFUNC_OK) { failf(data, "operation aborted by pre-request callback"); /* failure in pre-request callback - do not do any other processing */ @@ -2740,9 +2740,6 @@ static CURLMcode multi_runsingle(struct Curl_multi *multi, CURLMcode mresult; CURLcode result = CURLE_OK; - if(!GOOD_EASY_HANDLE(data)) - return CURLM_BAD_EASY_HANDLE; - if(multi->dead) { /* a multi-level callback returned error before, meaning every individual transfer now has failed */ @@ -2911,12 +2908,6 @@ static CURLMcode multi_perform(struct Curl_multi *multi, uint32_t mid; struct Curl_sigpipe_ctx sigpipe_ctx; - if(multi->in_callback) - return CURLM_RECURSIVE_API_CALL; - - if(multi->in_ntfy_callback) - return CURLM_RECURSIVE_API_CALL; - sigpipe_init(&sigpipe_ctx); if(Curl_uint32_bset_first(&multi->process, &mid)) { @@ -2987,32 +2978,35 @@ static CURLMcode multi_perform(struct Curl_multi *multi, CURLMcode curl_multi_perform(CURLM *m, int *running_handles) { - struct Curl_multi *multi = m; + struct Curl_mapi_guard guard; + CURLMcode mresult; - if(!GOOD_MULTI_HANDLE(multi)) - return CURLM_BAD_HANDLE; - - return multi_perform(multi, running_handles); + if(CURL_MAPI_ENTER(&guard, m, multi_perform, &mresult)) { + mresult = multi_perform(m, running_handles); + } + CURL_MAPI_LEAVE(&guard); + return mresult; } CURLMcode curl_multi_cleanup(CURLM *m) { - struct Curl_multi *multi = m; - if(GOOD_MULTI_HANDLE(multi)) { + struct Curl_mapi_guard guard; + CURLMcode mresult; + + if(CURL_MAPI_ENTER(&guard, m, multi_cleanup, &mresult)) { + struct Curl_multi *multi = m; void *entry; uint32_t mid; - if(multi->in_callback) - return CURLM_RECURSIVE_API_CALL; - if(multi->in_ntfy_callback) - return CURLM_RECURSIVE_API_CALL; /* First remove all remaining easy handles, * close internal ones. admin handle is special */ if(Curl_uint32_tbl_first(&multi->xfers, &mid, &entry)) { do { struct Curl_easy *data = entry; - if(!GOOD_EASY_HANDLE(data)) - return CURLM_BAD_HANDLE; + if(!GOOD_EASY_HANDLE(data)) { + mresult = CURLM_BAD_HANDLE; + goto out; + } #ifdef DEBUGBUILD if(mid != data->mid) { @@ -3089,9 +3083,11 @@ CURLMcode curl_multi_cleanup(CURLM *m) Curl_uint32_tbl_destroy(&multi->xfers); curlx_free(multi); - return CURLM_OK; + mresult = CURLM_OK; } - return CURLM_BAD_HANDLE; +out: + CURL_MAPI_LEAVE(&guard); + return mresult; } /* @@ -3106,30 +3102,32 @@ CURLMcode curl_multi_cleanup(CURLM *m) CURLMsg *curl_multi_info_read(CURLM *m, int *msgs_in_queue) { - struct Curl_message *msg; - struct Curl_multi *multi = m; + struct Curl_mapi_guard guard; + CURLMsg *msg_result = NULL; *msgs_in_queue = 0; /* default to none */ + if(CURL_MAPI_ENTER(&guard, m, multi_info_read, NULL)) { + struct Curl_multi *multi = m; + if(Curl_llist_count(&multi->msglist)) { + /* there is one or more messages in the list */ + struct Curl_llist_node *e; + struct Curl_message *msg; - if(GOOD_MULTI_HANDLE(multi) && - !multi->in_callback && - Curl_llist_count(&multi->msglist)) { - /* there is one or more messages in the list */ - struct Curl_llist_node *e; + /* extract the head of the list to return */ + e = Curl_llist_head(&multi->msglist); - /* extract the head of the list to return */ - e = Curl_llist_head(&multi->msglist); + msg = Curl_node_elem(e); - msg = Curl_node_elem(e); + /* remove the extracted entry */ + Curl_node_remove(e); - /* remove the extracted entry */ - Curl_node_remove(e); + *msgs_in_queue = curlx_uztosi(Curl_llist_count(&multi->msglist)); - *msgs_in_queue = curlx_uztosi(Curl_llist_count(&multi->msglist)); - - return &msg->extmsg; + msg_result = &msg->extmsg; + } } - return NULL; + CURL_MAPI_LEAVE(&guard); + return msg_result; } void Curl_multi_will_close(struct Curl_easy *data, curl_socket_t s) @@ -3357,126 +3355,125 @@ out: #undef curl_multi_setopt CURLMcode curl_multi_setopt(CURLM *m, CURLMoption option, ...) { + struct Curl_mapi_guard guard; CURLMcode mresult = CURLM_OK; - va_list param; - unsigned long uarg; - struct Curl_multi *multi = m; - if(!GOOD_MULTI_HANDLE(multi)) - return CURLM_BAD_HANDLE; + if(CURL_MAPI_ENTER(&guard, m, multi_setopt, &mresult)) { + struct Curl_multi *multi = m; + va_list param; + unsigned long uarg; - if(multi->in_callback) - return CURLM_RECURSIVE_API_CALL; + va_start(param, option); - va_start(param, option); - - switch(option) { - case CURLMOPT_SOCKETFUNCTION: - multi->socket_cb = va_arg(param, curl_socket_callback); - break; - case CURLMOPT_SOCKETDATA: - multi->socket_userp = va_arg(param, void *); - break; - case CURLMOPT_PUSHFUNCTION: - multi->push_cb = va_arg(param, curl_push_callback); - break; - case CURLMOPT_PUSHDATA: - multi->push_userp = va_arg(param, void *); - break; - case CURLMOPT_PIPELINING: - multi->multiplexing = va_arg(param, long) & CURLPIPE_MULTIPLEX ? 1 : 0; - break; - case CURLMOPT_TIMERFUNCTION: - multi->timer_cb = va_arg(param, curl_multi_timer_callback); - break; - case CURLMOPT_TIMERDATA: - multi->timer_userp = va_arg(param, void *); - break; - case CURLMOPT_MAXCONNECTS: - uarg = va_arg(param, unsigned long); - if(uarg <= UINT_MAX) - multi->maxconnects = (unsigned int)uarg; - break; - case CURLMOPT_MAX_HOST_CONNECTIONS: - if(!curlx_sltouz(va_arg(param, long), &multi->max_host_connections)) - mresult = CURLM_BAD_FUNCTION_ARGUMENT; - break; - case CURLMOPT_MAX_TOTAL_CONNECTIONS: - if(!curlx_sltouz(va_arg(param, long), &multi->max_total_connections)) - mresult = CURLM_BAD_FUNCTION_ARGUMENT; - break; - /* options formerly used for pipelining */ - case CURLMOPT_MAX_PIPELINE_LENGTH: - break; - case CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE: - break; - case CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE: - break; - case CURLMOPT_PIPELINING_SITE_BL: - break; - case CURLMOPT_PIPELINING_SERVER_BL: - break; - case CURLMOPT_MAX_CONCURRENT_STREAMS: { - long streams = va_arg(param, long); - if((streams < 1) || (streams > INT_MAX)) - streams = 100; - multi->max_concurrent_streams = (unsigned int)streams; - break; - } - case CURLMOPT_NETWORK_CHANGED: { - long val = va_arg(param, long); - if(val & CURLMNWC_CLEAR_ALL) - /* In the beginning, all values available to set were 1 by mistake. We - converted this to mean "all", thus setting all the bits - automatically */ - val = CURLMNWC_CLEAR_DNS | CURLMNWC_CLEAR_CONNS; - if(val & CURLMNWC_CLEAR_DNS) { - Curl_dnscache_clear(multi->admin); - } - if(val & CURLMNWC_CLEAR_CONNS) { - Curl_cpool_nw_changed(multi->admin); - } - break; - } - case CURLMOPT_NOTIFYFUNCTION: - multi->ntfy.ntfy_cb = va_arg(param, curl_notify_callback); - break; - case CURLMOPT_NOTIFYDATA: - multi->ntfy.ntfy_cb_data = va_arg(param, void *); - break; - case CURLMOPT_RESOLVE_THREADS_MAX: -#ifdef USE_RESOLV_THREADED - uarg = va_arg(param, long); - if((uarg <= 0) || (uarg > UINT32_MAX)) - mresult = CURLM_BAD_FUNCTION_ARGUMENT; - else { - CURLcode result = Curl_async_thrdd_multi_set_props( - multi, 0, (uint32_t)uarg, 2000); - switch(result) { - case CURLE_OK: - mresult = CURLM_OK; - break; - case CURLE_BAD_FUNCTION_ARGUMENT: + switch(option) { + case CURLMOPT_SOCKETFUNCTION: + multi->socket_cb = va_arg(param, curl_socket_callback); + break; + case CURLMOPT_SOCKETDATA: + multi->socket_userp = va_arg(param, void *); + break; + case CURLMOPT_PUSHFUNCTION: + multi->push_cb = va_arg(param, curl_push_callback); + break; + case CURLMOPT_PUSHDATA: + multi->push_userp = va_arg(param, void *); + break; + case CURLMOPT_PIPELINING: + multi->multiplexing = va_arg(param, long) & CURLPIPE_MULTIPLEX ? 1 : 0; + break; + case CURLMOPT_TIMERFUNCTION: + multi->timer_cb = va_arg(param, curl_multi_timer_callback); + break; + case CURLMOPT_TIMERDATA: + multi->timer_userp = va_arg(param, void *); + break; + case CURLMOPT_MAXCONNECTS: + uarg = va_arg(param, unsigned long); + if(uarg <= UINT_MAX) + multi->maxconnects = (unsigned int)uarg; + break; + case CURLMOPT_MAX_HOST_CONNECTIONS: + if(!curlx_sltouz(va_arg(param, long), &multi->max_host_connections)) mresult = CURLM_BAD_FUNCTION_ARGUMENT; - break; - case CURLE_OUT_OF_MEMORY: - mresult = CURLM_OUT_OF_MEMORY; - break; - default: - mresult = CURLM_INTERNAL_ERROR; - break; - } + break; + case CURLMOPT_MAX_TOTAL_CONNECTIONS: + if(!curlx_sltouz(va_arg(param, long), &multi->max_total_connections)) + mresult = CURLM_BAD_FUNCTION_ARGUMENT; + break; + /* options formerly used for pipelining */ + case CURLMOPT_MAX_PIPELINE_LENGTH: + break; + case CURLMOPT_CONTENT_LENGTH_PENALTY_SIZE: + break; + case CURLMOPT_CHUNK_LENGTH_PENALTY_SIZE: + break; + case CURLMOPT_PIPELINING_SITE_BL: + break; + case CURLMOPT_PIPELINING_SERVER_BL: + break; + case CURLMOPT_MAX_CONCURRENT_STREAMS: { + long streams = va_arg(param, long); + if((streams < 1) || (streams > INT_MAX)) + streams = 100; + multi->max_concurrent_streams = (unsigned int)streams; + break; } + case CURLMOPT_NETWORK_CHANGED: { + long val = va_arg(param, long); + if(val & CURLMNWC_CLEAR_ALL) + /* In the beginning, all values available to set were 1 by mistake. We + converted this to mean "all", thus setting all the bits + automatically */ + val = CURLMNWC_CLEAR_DNS | CURLMNWC_CLEAR_CONNS; + if(val & CURLMNWC_CLEAR_DNS) { + Curl_dnscache_clear(multi->admin); + } + if(val & CURLMNWC_CLEAR_CONNS) { + Curl_cpool_nw_changed(multi->admin); + } + break; + } + case CURLMOPT_NOTIFYFUNCTION: + multi->ntfy.ntfy_cb = va_arg(param, curl_notify_callback); + break; + case CURLMOPT_NOTIFYDATA: + multi->ntfy.ntfy_cb_data = va_arg(param, void *); + break; + case CURLMOPT_RESOLVE_THREADS_MAX: +#ifdef USE_RESOLV_THREADED + uarg = va_arg(param, long); + if((uarg <= 0) || (uarg > UINT32_MAX)) + mresult = CURLM_BAD_FUNCTION_ARGUMENT; + else { + CURLcode result = Curl_async_thrdd_multi_set_props( + multi, 0, (uint32_t)uarg, 2000); + switch(result) { + case CURLE_OK: + mresult = CURLM_OK; + break; + case CURLE_BAD_FUNCTION_ARGUMENT: + mresult = CURLM_BAD_FUNCTION_ARGUMENT; + break; + case CURLE_OUT_OF_MEMORY: + mresult = CURLM_OUT_OF_MEMORY; + break; + default: + mresult = CURLM_INTERNAL_ERROR; + break; + } + } #endif - break; - case CURLMOPT_QUICK_EXIT: - multi->quick_exit = va_arg(param, long) ? 1 : 0; - break; - default: - mresult = CURLM_UNKNOWN_OPTION; - break; + break; + case CURLMOPT_QUICK_EXIT: + multi->quick_exit = va_arg(param, long) ? 1 : 0; + break; + default: + mresult = CURLM_UNKNOWN_OPTION; + break; + } + va_end(param); } - va_end(param); + CURL_MAPI_LEAVE(&guard); return mresult; } @@ -3485,33 +3482,39 @@ CURLMcode curl_multi_setopt(CURLM *m, CURLMoption option, ...) CURLMcode curl_multi_socket(CURLM *m, curl_socket_t s, int *running_handles) { - struct Curl_multi *multi = m; - if(multi->in_callback) - return CURLM_RECURSIVE_API_CALL; - if(multi->in_ntfy_callback) - return CURLM_RECURSIVE_API_CALL; - return multi_socket(multi, FALSE, s, 0, running_handles); + struct Curl_mapi_guard guard; + CURLMcode mresult; + + if(CURL_MAPI_ENTER(&guard, m, multi_socket, &mresult)) { + mresult = multi_socket(m, FALSE, s, 0, running_handles); + } + CURL_MAPI_LEAVE(&guard); + return mresult; } CURLMcode curl_multi_socket_action(CURLM *m, curl_socket_t s, int ev_bitmask, int *running_handles) { - struct Curl_multi *multi = m; - if(multi->in_callback) - return CURLM_RECURSIVE_API_CALL; - if(multi->in_ntfy_callback) - return CURLM_RECURSIVE_API_CALL; - return multi_socket(multi, FALSE, s, ev_bitmask, running_handles); + struct Curl_mapi_guard guard; + CURLMcode mresult; + + if(CURL_MAPI_ENTER(&guard, m, multi_socket_action, &mresult)) { + mresult = multi_socket(m, FALSE, s, ev_bitmask, running_handles); + } + CURL_MAPI_LEAVE(&guard); + return mresult; } CURLMcode curl_multi_socket_all(CURLM *m, int *running_handles) { - struct Curl_multi *multi = m; - if(multi->in_callback) - return CURLM_RECURSIVE_API_CALL; - if(multi->in_ntfy_callback) - return CURLM_RECURSIVE_API_CALL; - return multi_socket(multi, TRUE, CURL_SOCKET_BAD, 0, running_handles); + struct Curl_mapi_guard guard; + CURLMcode mresult; + + if(CURL_MAPI_ENTER(&guard, m, multi_socket_all, &mresult)) { + mresult = multi_socket(m, TRUE, CURL_SOCKET_BAD, 0, running_handles); + } + CURL_MAPI_LEAVE(&guard); + return mresult; } static bool multi_has_dirties(struct Curl_multi *multi) @@ -3543,6 +3546,7 @@ static void multi_timeout(struct Curl_multi *multi, VERBOSE(struct Curl_easy *data = NULL); if(multi->dead) { + *expire_time = tv_zero; *timeout_ms = 0; return; } @@ -3599,18 +3603,17 @@ static void multi_timeout(struct Curl_multi *multi, CURLMcode curl_multi_timeout(CURLM *m, long *timeout_ms) { - struct curltime expire_time; - struct Curl_multi *multi = m; + struct Curl_mapi_guard guard; + CURLMcode mresult; - /* First, make some basic checks that the CURLM handle is a good handle */ - if(!GOOD_MULTI_HANDLE(multi)) - return CURLM_BAD_HANDLE; + if(CURL_MAPI_ENTER(&guard, m, multi_timeout, &mresult)) { + struct curltime expire_time; - if(multi->in_callback) - return CURLM_RECURSIVE_API_CALL; - - multi_timeout(multi, &expire_time, timeout_ms); - return CURLM_OK; + multi_timeout(m, &expire_time, timeout_ms); + mresult = CURLM_OK; + } + CURL_MAPI_LEAVE(&guard); + return mresult; } /* @@ -3619,7 +3622,7 @@ CURLMcode curl_multi_timeout(CURLM *m, */ CURLMcode Curl_update_timer(struct Curl_multi *multi) { - struct curltime expire_ts; + struct curltime expire_ts = { 0, 0 }; long timeout_ms; int rc; bool set_value = FALSE; @@ -3656,11 +3659,13 @@ CURLMcode Curl_update_timer(struct Curl_multi *multi) } if(set_value) { + struct Curl_mapi_guard guard; + multi->last_expire_ts = expire_ts; multi->last_timeout_ms = timeout_ms; - set_in_callback(multi, TRUE); + CURL_CBAPI_MULTI_START(&guard, multi, multi_timer_cb); rc = multi->timer_cb(multi, timeout_ms, multi->timer_userp); - set_in_callback(multi, FALSE); + CURL_CBAPI_MULTI_END(&guard); if(rc == -1) { multi->dead = TRUE; return CURLM_ABORTED_BY_CALLBACK; @@ -3858,11 +3863,14 @@ void Curl_expire_clear(struct Curl_easy *data) CURLMcode curl_multi_assign(CURLM *m, curl_socket_t sockfd, void *sockp) { - struct Curl_multi *multi = m; - if(!GOOD_MULTI_HANDLE(multi)) - return CURLM_BAD_HANDLE; + struct Curl_mapi_guard guard; + CURLMcode mresult; - return Curl_multi_ev_assign(multi, sockfd, sockp); + if(CURL_MAPI_ENTER(&guard, m, multi_assign, &mresult)) { + mresult = Curl_multi_ev_assign(m, sockfd, sockp); + } + CURL_MAPI_LEAVE(&guard); + return mresult; } static void move_pending_to_connect(struct Curl_multi *multi, @@ -3928,18 +3936,6 @@ static void process_pending_handles(struct Curl_multi *multi) } } -/* 'value' used to be a boolean but can now also contain more info */ -void Curl_set_in_callback(struct Curl_easy *data, uint8_t value) -{ - if(data && data->multi) - data->multi->in_callback = value; -} - -uint8_t Curl_is_in_callback(struct Curl_easy *data) -{ - return (data && data->multi) ? data->multi->in_callback : IN_CALLBACK_NO; -} - unsigned int Curl_multi_max_concurrent_streams(struct Curl_multi *multi) { DEBUGASSERT(multi); @@ -3948,24 +3944,31 @@ unsigned int Curl_multi_max_concurrent_streams(struct Curl_multi *multi) CURL **curl_multi_get_handles(CURLM *m) { - struct Curl_multi *multi = m; - void *entry; - size_t count = Curl_uint32_tbl_count(&multi->xfers); - CURL **a = curlx_malloc(sizeof(struct Curl_easy *) * (count + 1)); - if(a) { - unsigned int i = 0; - uint32_t mid; + struct Curl_mapi_guard guard; + CURL **a = NULL; - if(Curl_uint32_tbl_first(&multi->xfers, &mid, &entry)) { - do { - struct Curl_easy *data = entry; - DEBUGASSERT(i < count); - if(!data->state.internal) - a[i++] = data; - } while(Curl_uint32_tbl_next(&multi->xfers, mid, &mid, &entry)); + if(CURL_MAPI_ENTER(&guard, m, multi_get_handles, NULL)) { + struct Curl_multi *multi = m; + void *entry; + size_t count = Curl_uint32_tbl_count(&multi->xfers); + + a = curlx_malloc(sizeof(struct Curl_easy *) * (count + 1)); + if(a) { + unsigned int i = 0; + uint32_t mid; + + if(Curl_uint32_tbl_first(&multi->xfers, &mid, &entry)) { + do { + struct Curl_easy *data = entry; + DEBUGASSERT(i < count); + if(!data->state.internal) + a[i++] = data; + } while(Curl_uint32_tbl_next(&multi->xfers, mid, &mid, &entry)); + } + a[i] = NULL; /* last entry is a NULL */ } - a[i] = NULL; /* last entry is a NULL */ } + CURL_MAPI_LEAVE(&guard); return a; } @@ -3973,40 +3976,49 @@ CURLMcode curl_multi_get_offt(CURLM *m, CURLMinfo_offt info, curl_off_t *pvalue) { - struct Curl_multi *multi = m; - uint32_t n; + struct Curl_mapi_guard guard; + CURLMcode mresult = CURLM_OK; - if(!GOOD_MULTI_HANDLE(multi)) - return CURLM_BAD_HANDLE; - if(!pvalue) - return CURLM_BAD_FUNCTION_ARGUMENT; + if(CURL_MAPI_ENTER(&guard, m, multi_get_offt, &mresult)) { + struct Curl_multi *multi = m; + uint32_t n; - switch(info) { - case CURLMINFO_XFERS_CURRENT: - n = Curl_uint32_tbl_count(&multi->xfers); - if(n && multi->admin) - --n; - *pvalue = (curl_off_t)n; - return CURLM_OK; - case CURLMINFO_XFERS_RUNNING: - n = Curl_uint32_bset_count(&multi->process); - if(n && Curl_uint32_bset_contains(&multi->process, multi->admin->mid)) - --n; - *pvalue = (curl_off_t)n; - return CURLM_OK; - case CURLMINFO_XFERS_PENDING: - *pvalue = (curl_off_t)Curl_uint32_bset_count(&multi->pending); - return CURLM_OK; - case CURLMINFO_XFERS_DONE: - *pvalue = (curl_off_t)Curl_uint32_bset_count(&multi->msgsent); - return CURLM_OK; - case CURLMINFO_XFERS_ADDED: - *pvalue = multi->xfers_total_ever; - return CURLM_OK; - default: - *pvalue = -1; - return CURLM_UNKNOWN_OPTION; + if(!pvalue) { + mresult = CURLM_BAD_FUNCTION_ARGUMENT; + goto out; + } + + switch(info) { + case CURLMINFO_XFERS_CURRENT: + n = Curl_uint32_tbl_count(&multi->xfers); + if(n && multi->admin) + --n; + *pvalue = (curl_off_t)n; + break; + case CURLMINFO_XFERS_RUNNING: + n = Curl_uint32_bset_count(&multi->process); + if(n && Curl_uint32_bset_contains(&multi->process, multi->admin->mid)) + --n; + *pvalue = (curl_off_t)n; + break; + case CURLMINFO_XFERS_PENDING: + *pvalue = (curl_off_t)Curl_uint32_bset_count(&multi->pending); + break; + case CURLMINFO_XFERS_DONE: + *pvalue = (curl_off_t)Curl_uint32_bset_count(&multi->msgsent); + break; + case CURLMINFO_XFERS_ADDED: + *pvalue = multi->xfers_total_ever; + break; + default: + *pvalue = -1; + mresult = CURLM_UNKNOWN_OPTION; + break; + } } +out: + CURL_MAPI_LEAVE(&guard); + return mresult; } CURLcode Curl_multi_xfer_buf_borrow(struct Curl_easy *data, @@ -4190,6 +4202,11 @@ struct Curl_easy *Curl_multi_get_easy(struct Curl_multi *multi, return NULL; } +bool Curl_multi_knows_easy(struct Curl_multi *multi, struct Curl_easy *data) +{ + return Curl_uint32_tbl_get(&multi->xfers, data->mid) == data; +} + unsigned int Curl_multi_xfers_running(struct Curl_multi *multi) { DEBUGASSERT(multi); @@ -4212,20 +4229,26 @@ void Curl_multi_clear_dirty(struct Curl_easy *data) CURLMcode curl_multi_notify_enable(CURLM *m, unsigned int notification) { - struct Curl_multi *multi = m; + struct Curl_mapi_guard guard; + CURLMcode mresult = CURLM_OK; - if(!GOOD_MULTI_HANDLE(multi)) - return CURLM_BAD_HANDLE; - return Curl_mntfy_enable(multi, notification); + if(CURL_MAPI_ENTER(&guard, m, multi_notify_enable, &mresult)) { + mresult = Curl_mntfy_enable(m, notification); + } + CURL_MAPI_LEAVE(&guard); + return mresult; } CURLMcode curl_multi_notify_disable(CURLM *m, unsigned int notification) { - struct Curl_multi *multi = m; + struct Curl_mapi_guard guard; + CURLMcode mresult = CURLM_OK; - if(!GOOD_MULTI_HANDLE(multi)) - return CURLM_BAD_HANDLE; - return Curl_mntfy_disable(multi, notification); + if(CURL_MAPI_ENTER(&guard, m, multi_notify_disable, &mresult)) { + mresult = Curl_mntfy_disable(m, notification); + } + CURL_MAPI_LEAVE(&guard); + return mresult; } #ifdef DEBUGBUILD diff --git a/lib/multi_ev.c b/lib/multi_ev.c index 12d1ca2abb..3b7eeb0dc3 100644 --- a/lib/multi_ev.c +++ b/lib/multi_ev.c @@ -34,11 +34,6 @@ #include "uint-spbset.h" #include "multihandle.h" -static void mev_in_callback(struct Curl_multi *multi, uint8_t value) -{ - multi->in_callback = value; -} - #ifdef DEBUGBUILD #define SH_ENTRY_MAGIC 0x570091d #endif @@ -206,12 +201,14 @@ static CURLMcode mev_forget_socket(struct Curl_multi *multi, /* We managed this socket before, tell the socket callback to forget it. */ if(entry->announced && multi->socket_cb) { + struct Curl_mapi_guard guard; + NOVERBOSE((void)cause); CURL_TRC_M(data, "ev %s, call(fd=%" FMT_SOCKET_T ", ev=REMOVE)", cause, s); - mev_in_callback(multi, IN_CALLBACK_FORBID_EASY_PAUSE); + CURL_CBAPI_MULTI_START(&guard, multi, multi_socket_cb); rc = multi->socket_cb(data, s, CURL_POLL_REMOVE, multi->socket_userp, entry->user_data); - mev_in_callback(multi, IN_CALLBACK_NO); + CURL_CBAPI_END(&guard); entry = mev_sh_entry_get(&multi->ev.sh_entries, s); if(entry) entry->announced = FALSE; @@ -282,10 +279,13 @@ static CURLMcode mev_sh_entry_update(struct Curl_multi *multi, CURL_TRC_M(data, "ev update call(fd=%" FMT_SOCKET_T ", ev=%s%s)", s, (comboaction & CURL_POLL_IN) ? "IN" : "", (comboaction & CURL_POLL_OUT) ? "OUT" : ""); - mev_in_callback(multi, IN_CALLBACK_FORBID_EASY_PAUSE); - rc = multi->socket_cb(data, s, comboaction, multi->socket_userp, - entry->user_data); - mev_in_callback(multi, IN_CALLBACK_NO); + { + struct Curl_mapi_guard guard; + CURL_CBAPI_MULTI_START(&guard, multi, multi_socket_cb); + rc = multi->socket_cb(data, s, comboaction, multi->socket_userp, + entry->user_data); + CURL_CBAPI_MULTI_END(&guard); + } if(rc == -1) { multi->dead = TRUE; return CURLM_ABORTED_BY_CALLBACK; diff --git a/lib/multi_ntfy.c b/lib/multi_ntfy.c index 1319aaec07..48c3723951 100644 --- a/lib/multi_ntfy.c +++ b/lib/multi_ntfy.c @@ -179,8 +179,13 @@ void Curl_mntfy_add(struct Curl_easy *data, unsigned int type) CURLMcode Curl_mntfy_dispatch_all(struct Curl_multi *multi) { - DEBUGASSERT(!multi->in_ntfy_callback); - multi->in_ntfy_callback = TRUE; + struct Curl_mapi_guard guard; + + if(!multi) + return CURLM_BAD_FUNCTION_ARGUMENT; + + CURL_CBAPI_MULTI_START(&guard, multi, multi_ntfy_cb); + while(multi->ntfy.head && !multi->ntfy.failure) { struct mntfy_chunk *chunk = multi->ntfy.head; /* this may cause new notifications to be added! */ @@ -194,7 +199,8 @@ CURLMcode Curl_mntfy_dispatch_all(struct Curl_multi *multi) multi->ntfy.head = chunk->next; mnfty_chunk_destroy(chunk); } - multi->in_ntfy_callback = FALSE; + + CURL_CBAPI_MULTI_END(&guard); if(multi->ntfy.failure) { CURLMcode mresult = multi->ntfy.failure; diff --git a/lib/multihandle.h b/lib/multihandle.h index 1af07fa02b..b0c10e92c1 100644 --- a/lib/multihandle.h +++ b/lib/multihandle.h @@ -23,6 +23,7 @@ * SPDX-License-Identifier: curl * ***************************************************************************/ +#include "api.h" #include "llist.h" #include "hash.h" #include "conncache.h" @@ -45,10 +46,6 @@ struct Curl_message { struct CURLMsg extmsg; }; -#define IN_CALLBACK_NO 0 -#define IN_CALLBACK_YES 1 -#define IN_CALLBACK_FORBID_EASY_PAUSE 2 - /* NOTE: if you add a state here, add the name to the statenames[] array * in curl_trc.c as well! */ @@ -89,14 +86,14 @@ typedef enum { /* This is the struct known as CURLM on the outside */ struct Curl_multi { /* First a simple identifier to easier detect if a user mix up - this multi handle with an easy handle. Set this to CURL_MULTI_HANDLE. */ - unsigned int magic; + this multi handle with an easy handle. + Set this to CURLMULTI_MAGIC_NUMBER. */ + uint32_t magic; + uint32_t xfers_alive; /* amount of added transfers that have + not yet reached COMPLETE state */ + uint32_t xfers_really_alive; /* amount of added transfers that have + passed INIT state but are not COMPLETE yet */ - unsigned int xfers_alive; /* amount of added transfers that have - not yet reached COMPLETE state */ - unsigned int xfers_really_alive; /* amount of added transfers that have - passed INIT state but are not COMPLETE yet */ - curl_off_t xfers_total_ever; /* total of added transfers, ever. */ struct uint32_tbl xfers; /* transfers added to this multi */ /* Each transfer's mid may be present in at most one of these */ struct uint32_bset process; /* transfer being processed */ @@ -104,8 +101,12 @@ struct Curl_multi { struct uint32_bset pending; /* transfers in waiting (conn limit etc.) */ struct uint32_bset msgsent; /* transfers done with message for application */ + struct Curl_mapi_stack callstack; /* multi api calls ongoing */ + struct Curl_llist msglist; /* a list of messages from completed transfers */ + curl_off_t xfers_total_ever; /* total of added transfers, ever. */ + struct Curl_easy *admin; /* internal easy handle for admin operations. gets assigned `mid` 0 on multi init */ @@ -194,11 +195,9 @@ struct Curl_multi { #endif uint32_t last_pending_mid; /* mid of last pending transfer rescheduled */ uint32_t last_resolv_id; /* id of the last DNS resolve operation */ - uint8_t in_callback; /* conditions for executing in callbacks */ BIT(ipv6_works); BIT(multiplexing); /* multiplexing wanted */ BIT(recheckstate); /* see Curl_multi_connchanged */ - BIT(in_ntfy_callback); /* true while dispatching notifications */ #ifdef USE_OPENSSL BIT(ssl_seeded); #endif diff --git a/lib/multiif.h b/lib/multiif.h index 023d3253aa..aaac33e942 100644 --- a/lib/multiif.h +++ b/lib/multiif.h @@ -37,13 +37,16 @@ void Curl_attach_connection(struct Curl_easy *data, struct connectdata *conn); void Curl_detach_connection(struct Curl_easy *data); bool Curl_multiplex_wanted(const struct Curl_multi *multi); -void Curl_set_in_callback(struct Curl_easy *data, uint8_t value); -uint8_t Curl_is_in_callback(struct Curl_easy *data); CURLcode Curl_preconnect(struct Curl_easy *data); bool Curl_is_connecting(struct Curl_easy *data); void Curl_multi_connchanged(struct Curl_multi *multi); +CURLMcode Curl_multi_add_handle(struct Curl_multi *multi, + struct Curl_easy *data); +CURLMcode Curl_multi_remove_handle(struct Curl_multi *multi, + struct Curl_easy *data); + /* Internal version of curl_multi_init() accepts size parameters for the socket, connection and dns hashes */ struct Curl_multi *Curl_multi_handle(uint32_t xfer_table_size, @@ -148,11 +151,14 @@ void Curl_multi_xfer_sockbuf_release(struct Curl_easy *data, char *buf); /** * Get the easy handle for the given mid. - * Returns NULL if not found. + * Returns NULL if not found or not a GOOD_EASY_HANDLE() */ struct Curl_easy *Curl_multi_get_easy(struct Curl_multi *multi, uint32_t mid); +/* TRUE if multi knows about data via its `mid` */ +bool Curl_multi_knows_easy(struct Curl_multi *multi, struct Curl_easy *data); + /* Get the # of transfers current in process/pending. */ unsigned int Curl_multi_xfers_running(struct Curl_multi *multi); diff --git a/lib/progress.c b/lib/progress.c index 72d03edf2d..45e3db14f5 100644 --- a/lib/progress.c +++ b/lib/progress.c @@ -659,13 +659,14 @@ static CURLcode pgrsupdate(struct Curl_easy *data, bool showprogress) int rc; if(data->set.fxferinfo) { /* There is a callback set, call that */ - Curl_set_in_callback(data, TRUE); + struct Curl_mapi_guard guard; + CURL_CBAPI_START(&guard, data, easy_fxferinfo); rc = data->set.fxferinfo(data->set.progress_client, data->progress.dl.total_size, data->progress.dl.cur_size, data->progress.ul.total_size, data->progress.ul.cur_size); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); if(rc != CURL_PROGRESSFUNC_CONTINUE) { if(rc) { failf(data, "Callback aborted"); @@ -676,13 +677,14 @@ static CURLcode pgrsupdate(struct Curl_easy *data, bool showprogress) } else if(data->set.fprogress) { /* The older deprecated callback is set, call that */ - Curl_set_in_callback(data, TRUE); + struct Curl_mapi_guard guard; + CURL_CBAPI_START(&guard, data, easy_fprogress); rc = data->set.fprogress(data->set.progress_client, (double)data->progress.dl.total_size, (double)data->progress.dl.cur_size, (double)data->progress.ul.total_size, (double)data->progress.ul.cur_size); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); if(rc != CURL_PROGRESSFUNC_CONTINUE) { if(rc) { failf(data, "Callback aborted"); diff --git a/lib/rtsp.c b/lib/rtsp.c index 8c5cdd5643..bc8d32622a 100644 --- a/lib/rtsp.c +++ b/lib/rtsp.c @@ -598,6 +598,7 @@ static CURLcode rtp_write_body_junk(struct Curl_easy *data, static CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr, size_t len) { + struct Curl_mapi_guard guard; size_t wrote; curl_write_callback writeit; void *user_ptr; @@ -620,9 +621,9 @@ static CURLcode rtp_client_write(struct Curl_easy *data, const char *ptr, user_ptr = data->set.out; } - Curl_set_in_callback(data, TRUE); + CURL_CBAPI_START(&guard, data, easy_fwrite_rtp); wrote = writeit((char *)CURL_UNCONST(ptr), 1, len, user_ptr); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); if(wrote == CURL_WRITEFUNC_PAUSE) { failf(data, "Cannot pause RTP"); diff --git a/lib/sendf.c b/lib/sendf.c index 114b550578..449a4bc447 100644 --- a/lib/sendf.c +++ b/lib/sendf.c @@ -678,9 +678,10 @@ static CURLcode cr_in_read(struct Curl_easy *data, } nread = 0; if(ctx->read_cb && blen) { - Curl_set_in_callback(data, TRUE); + struct Curl_mapi_guard guard; + CURL_CBAPI_START(&guard, data, easy_cr_in_read); nread = ctx->read_cb(buf, 1, blen, ctx->cb_user_data); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); ctx->has_used_cb = TRUE; } @@ -778,9 +779,10 @@ static CURLcode cr_in_resume_from(struct Curl_easy *data, return CURLE_READ_ERROR; if(data->set.seek_func) { - Curl_set_in_callback(data, TRUE); + struct Curl_mapi_guard guard; + CURL_CBAPI_START(&guard, data, easy_seek_func); seekerr = data->set.seek_func(data->set.seek_client, offset, SEEK_SET); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); } if(seekerr != CURL_SEEKFUNC_OK) { @@ -792,6 +794,7 @@ static CURLcode cr_in_resume_from(struct Curl_easy *data, } /* when seekerr == CURL_SEEKFUNC_CANTSEEK (cannot seek to offset) */ do { + struct Curl_mapi_guard guard; char scratch[4 * 1024]; size_t readthisamountnow = (offset - passed > (curl_off_t)sizeof(scratch)) ? @@ -799,10 +802,10 @@ static CURLcode cr_in_resume_from(struct Curl_easy *data, curlx_sotouz(offset - passed); size_t actuallyread; - Curl_set_in_callback(data, TRUE); + CURL_CBAPI_START(&guard, data, easy_cr_in_resume_from); actuallyread = ctx->read_cb(scratch, 1, readthisamountnow, ctx->cb_user_data); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); passed += actuallyread; if((actuallyread == 0) || (actuallyread > readthisamountnow)) { @@ -838,11 +841,12 @@ static CURLcode cr_in_rewind(struct Curl_easy *data, return CURLE_OK; if(data->set.seek_func) { + struct Curl_mapi_guard guard; int err; - Curl_set_in_callback(data, TRUE); + CURL_CBAPI_START(&guard, data, easy_seek_func); err = (data->set.seek_func)(data->set.seek_client, 0, SEEK_SET); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); CURL_TRC_READ(data, "cr_in, rewind via set.seek_func -> %d", err); if(err) { failf(data, "seek callback returned error %d", err); @@ -850,12 +854,13 @@ static CURLcode cr_in_rewind(struct Curl_easy *data, } } else if(data->set.ioctl_func) { + struct Curl_mapi_guard guard; curlioerr err; - Curl_set_in_callback(data, TRUE); + CURL_CBAPI_START(&guard, data, easy_ioctl_func); err = (data->set.ioctl_func)(data, CURLIOCMD_RESTARTREAD, data->set.ioctl_client); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); CURL_TRC_READ(data, "cr_in, rewind via set.ioctl_func -> %d", (int)err); if(err) { failf(data, "ioctl callback returned error %d", (int)err); diff --git a/lib/setopt.c b/lib/setopt.c index 9a87e748d2..e6a067c5e6 100644 --- a/lib/setopt.c +++ b/lib/setopt.c @@ -2916,23 +2916,24 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param) * NOTE: This is one of few API functions that are allowed to be called from * within a callback. */ - #undef curl_easy_setopt CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...) { - va_list arg; + struct Curl_eapi_guard guard; CURLcode result; - struct Curl_easy *data = curl; - if(!data) - return CURLE_BAD_FUNCTION_ARGUMENT; + if(CURL_EAPI_ENTER(&guard, curl, easy_setopt, &result)) { + struct Curl_easy *data = curl; + va_list arg; - va_start(arg, option); + va_start(arg, option); - result = Curl_vsetopt(data, option, arg); + result = Curl_vsetopt(data, option, arg); - va_end(arg); - if(result == CURLE_BAD_FUNCTION_ARGUMENT) - failf(data, "setopt 0x%x got bad argument", (unsigned int)option); + va_end(arg); + if(result == CURLE_BAD_FUNCTION_ARGUMENT) + failf(data, "setopt 0x%x got bad argument", (unsigned int)option); + } + CURL_EAPI_LEAVE(&guard); return result; } diff --git a/lib/transfer.h b/lib/transfer.h index 7507ce27bd..9bdc93f3ed 100644 --- a/lib/transfer.h +++ b/lib/transfer.h @@ -147,4 +147,8 @@ CURLcode Curl_xfer_pause_recv(struct Curl_easy *data, bool enable); * use a forward proxy. */ bool Curl_xfer_is_secure(struct Curl_easy *data); +/* Internal variant of the API function */ +CURLcode Curl_easy_recv(struct Curl_easy *data, + void *buffer, size_t buflen, size_t *n); + #endif /* HEADER_CURL_TRANSFER_H */ diff --git a/lib/url.c b/lib/url.c index dd4e33c9d1..4ec31a3e5b 100644 --- a/lib/url.c +++ b/lib/url.c @@ -206,7 +206,7 @@ CURLcode Curl_close(struct Curl_easy **datap) /* This handle is still part of a multi handle, take care of this first and detach this handle from there. This detaches the connection. */ - curl_multi_remove_handle(data->multi, data); + Curl_multi_remove_handle(data->multi, data); } else { /* Detach connection if any is left. This should not be normal, but can be diff --git a/lib/urldata.h b/lib/urldata.h index 508ab729c1..16179318c0 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -53,6 +53,7 @@ #include "curlx/timeval.h" +#include "api.h" #include "asyn.h" #include "cookie.h" #include "creds.h" @@ -132,19 +133,6 @@ typedef CURLcode (Curl_recv)(struct Curl_easy *data, /* transfer */ #define UPLOADBUFFER_MAX (2 * 1024 * 1024) #define UPLOADBUFFER_MIN CURL_MAX_WRITE_SIZE -#define CURLEASY_MAGIC_NUMBER 0xc0dedbadU -#ifdef DEBUGBUILD -/* On a debug build, we want to fail hard on easy handles that - * are not NULL, but no longer have the MAGIC touch. This gives - * us early warning on things only discovered by valgrind otherwise. */ -#define GOOD_EASY_HANDLE(x) \ - (((x) && ((x)->magic == CURLEASY_MAGIC_NUMBER)) ? TRUE : \ - (DEBUGASSERT(!(x)), FALSE)) -#else -#define GOOD_EASY_HANDLE(x) \ - ((x) && ((x)->magic == CURLEASY_MAGIC_NUMBER)) -#endif - #ifdef USE_WINDOWS_SSPI #include "curl_sspi.h" #endif @@ -1224,6 +1212,26 @@ struct Curl_easy { /* First a simple identifier to easier detect if a user mix up this easy handle with a multi handle. Set this to CURLEASY_MAGIC_NUMBER */ uint32_t magic; + /* once an easy handle is added to a multi, either explicitly by the + * libcurl application or implicitly during `curl_easy_perform()`, + * a unique identifier inside this one multi instance. */ + uint32_t mid; + CURLMstate mstate; /* the handle's state */ + CURLcode result; /* previous result */ + + struct connectdata *conn; + struct Curl_multi *multi; /* if non-NULL, points to the multi handle + struct to which this "belongs" when used by + the multi interface */ + struct Curl_eapi_stack callstack; /* easy api calls ongoing */ + + struct Curl_share *share; /* Share, handles global variable mutexing */ + + struct Curl_multi *multi_easy; /* if non-NULL, points to the multi handle + struct to which this "belongs" when used + by the easy interface */ + struct Curl_message msg; /* A single posted message. */ + /* once an easy handle is tied to a connection pool a non-negative number to distinguish this transfer from other using the same pool. For easier tracking in log output. This may wrap around after LONG_MAX to 0 again, @@ -1231,28 +1239,9 @@ struct Curl_easy { uniqueness either IFF more than one connection pool is used by the libcurl application. */ curl_off_t id; - /* once an easy handle is added to a multi, either explicitly by the - * libcurl application or implicitly during `curl_easy_perform()`, - * a unique identifier inside this one multi instance. */ - uint32_t mid; uint32_t master_mid; /* if set, this transfer belongs to a master */ multi_sub_xfer_done_cb *sub_xfer_done; - struct connectdata *conn; - - CURLMstate mstate; /* the handle's state */ - CURLcode result; /* previous result */ - - struct Curl_message msg; /* A single posted message. */ - - struct Curl_multi *multi; /* if non-NULL, points to the multi handle - struct to which this "belongs" when used by - the multi interface */ - struct Curl_multi *multi_easy; /* if non-NULL, points to the multi handle - struct to which this "belongs" when used - by the easy interface */ - struct Curl_share *share; /* Share, handles global variable mutexing */ - /* `meta_hash` is a general key-value store for implementations * with the lifetime of the easy handle. * Elements need to be added with their own destructor to be invoked when diff --git a/lib/vssh/libssh.c b/lib/vssh/libssh.c index 3d1203d3b6..89d2f33bcc 100644 --- a/lib/vssh/libssh.c +++ b/lib/vssh/libssh.c @@ -290,6 +290,8 @@ static int myssh_is_known(struct Curl_easy *data, struct ssh_conn *sshc) } if(func) { /* use callback to determine action */ + struct Curl_mapi_guard guard; + rc = ssh_pki_export_pubkey_base64(pubkey, &found_base64); if(rc != SSH_OK) goto cleanup; @@ -321,11 +323,11 @@ static int myssh_is_known(struct Curl_easy *data, struct ssh_conn *sshc) goto cleanup; } - Curl_set_in_callback(data, TRUE); + CURL_CBAPI_START(&guard, data, easy_ssh_keyfunc); rc = func(data, knownkeyp, /* from the knownhosts file */ &foundkey, /* from the remote host */ keymatch, data->set.ssh_keyfunc_userp); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); switch(rc) { case CURLKHSTAT_FINE_ADD_TO_FILE: @@ -1097,10 +1099,11 @@ static int myssh_in_UPLOAD_INIT(struct Curl_easy *data, int seekerr = CURL_SEEKFUNC_OK; /* Let's read off the proper amount of bytes from the input. */ if(data->set.seek_func) { - Curl_set_in_callback(data, TRUE); + struct Curl_mapi_guard guard; + CURL_CBAPI_START(&guard, data, easy_seek_func); seekerr = data->set.seek_func(data->set.seek_client, data->state.resume_from, SEEK_SET); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); } if(seekerr != CURL_SEEKFUNC_OK) { diff --git a/lib/vssh/libssh2.c b/lib/vssh/libssh2.c index 5f13533033..672e9437c0 100644 --- a/lib/vssh/libssh2.c +++ b/lib/vssh/libssh2.c @@ -330,6 +330,7 @@ static CURLcode ssh_knownhost(struct Curl_easy *data, * What hostname does OpenSSH store in its file if an IDN name is * used? */ + struct Curl_mapi_guard guard; enum curl_khmatch keymatch; curl_sshkeycallback func = data->set.ssh_keyfunc ? data->set.ssh_keyfunc : sshkeycallback; @@ -402,11 +403,11 @@ static CURLcode ssh_knownhost(struct Curl_easy *data, keymatch = (enum curl_khmatch)keycheck; /* Ask the callback how to behave */ - Curl_set_in_callback(data, TRUE); + CURL_CBAPI_START(&guard, data, easy_ssh_keyfunc); rc = func(data, knownkeyp, /* from the knownhosts file */ &foundkey, /* from the remote host */ keymatch, data->set.ssh_keyfunc_userp); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); } } else { @@ -595,11 +596,12 @@ static CURLcode ssh_check_fingerprint(struct Curl_easy *data, const char *remotekey = libssh2_session_hostkey(sshc->ssh_session, &keylen, &sshkeytype); if(remotekey) { + struct Curl_mapi_guard guard; enum curl_khtype keytype = convert_ssh2_keytype(sshkeytype); - Curl_set_in_callback(data, TRUE); + CURL_CBAPI_START(&guard, data, easy_ssh_hostkeyfunc); rc = data->set.ssh_hostkeyfunc(data->set.ssh_hostkeyfunc_userp, (int)keytype, remotekey, keylen); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); if(rc != CURLKHMATCH_OK) { myssh_to(data, sshc, SSH_SESSION_FREE); failf(data, "SSH: callback failed host public key verification"); @@ -1028,10 +1030,11 @@ static CURLcode sftp_upload_init(struct Curl_easy *data, int seekerr = CURL_SEEKFUNC_OK; /* Let's read off the proper amount of bytes from the input. */ if(data->set.seek_func) { - Curl_set_in_callback(data, TRUE); + struct Curl_mapi_guard guard; + CURL_CBAPI_START(&guard, data, easy_seek_func); seekerr = data->set.seek_func(data->set.seek_client, data->state.resume_from, SEEK_SET); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); } if(seekerr != CURL_SEEKFUNC_OK) { @@ -1043,6 +1046,7 @@ static CURLcode sftp_upload_init(struct Curl_easy *data, } /* seekerr == CURL_SEEKFUNC_CANTSEEK (cannot seek to offset) */ do { + struct Curl_mapi_guard guard; char scratch[4 * 1024]; size_t readthisamountnow = (data->state.resume_from - passed > @@ -1050,11 +1054,11 @@ static CURLcode sftp_upload_init(struct Curl_easy *data, sizeof(scratch) : curlx_sotouz(data->state.resume_from - passed); size_t actuallyread; - Curl_set_in_callback(data, TRUE); + CURL_CBAPI_START(&guard, data, easy_fread_func); actuallyread = data->state.fread_func(scratch, 1, readthisamountnow, data->state.in); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); passed += actuallyread; if((actuallyread == 0) || (actuallyread > readthisamountnow)) { diff --git a/lib/vtls/openssl.c b/lib/vtls/openssl.c index 094fc3a374..23114937ae 100644 --- a/lib/vtls/openssl.c +++ b/lib/vtls/openssl.c @@ -3961,6 +3961,7 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, /* give application a chance to interfere with SSL set up. */ if(data->set.ssl.fsslctx) { + struct Curl_mapi_guard guard; /* When a user callback is installed to modify the SSL_CTX, * we need to do the full initialization before calling it. * See: #11800 */ @@ -3970,10 +3971,10 @@ CURLcode Curl_ossl_ctx_init(struct ossl_ctx *octx, return result; octx->x509_store_setup = TRUE; } - Curl_set_in_callback(data, TRUE); + CURL_CBAPI_START(&guard, data, easy_fsslctx); result = (*data->set.ssl.fsslctx)(data, octx->ssl_ctx, data->set.ssl.fsslctxp); - Curl_set_in_callback(data, FALSE); + CURL_CBAPI_END(&guard); if(result) { failf(data, "error signaled by SSL ctx callback"); return result; diff --git a/lib/ws.c b/lib/ws.c index 3082860389..335986c06a 100644 --- a/lib/ws.c +++ b/lib/ws.c @@ -762,7 +762,7 @@ static CURLcode ws_cw_write(struct Curl_easy *data, out: if(!result) { - result = ws_flush(data, ws, Curl_is_in_callback(data)); + result = ws_flush(data, ws, Curl_api_is_in_callback(data)); if(result == CURLE_AGAIN) result = CURLE_OK; } @@ -1110,7 +1110,7 @@ static CURLcode ws_enc_send(struct Curl_easy *data, } } else { - result = ws_flush(data, ws, Curl_is_in_callback(data)); + result = ws_flush(data, ws, Curl_api_is_in_callback(data)); if(result) return result; @@ -1143,7 +1143,7 @@ static CURLcode ws_enc_send(struct Curl_easy *data, } /* flush, blocking when in callback */ - result = ws_flush(data, ws, Curl_is_in_callback(data)); + result = ws_flush(data, ws, Curl_api_is_in_callback(data)); if(!result && ws->sendbuf_payload > 0) { *pnsent += ws->sendbuf_payload; buffer += ws->sendbuf_payload; @@ -1581,104 +1581,116 @@ static CURLcode nw_in_recv(void *reader_ctx, size_t *pnread) { struct Curl_easy *data = reader_ctx; - return curl_easy_recv(data, buf, buflen, pnread); + return Curl_easy_recv(data, buf, buflen, pnread); } CURLcode curl_ws_recv(CURL *curl, void *buffer, size_t buflen, size_t *recv, const struct curl_ws_frame **metap) { - struct Curl_easy *data = curl; - struct connectdata *conn; - struct websocket *ws; - struct ws_collect ctx; + struct Curl_eapi_guard guard; + CURLcode result = CURLE_OK; *recv = 0; *metap = NULL; - if(!GOOD_EASY_HANDLE(data) || (buflen && !buffer)) - return CURLE_BAD_FUNCTION_ARGUMENT; + if(CURL_EAPI_ENTER(&guard, curl, ws_recv, &result)) { + struct Curl_easy *data = curl; + struct connectdata *conn; + struct websocket *ws; + struct ws_collect ctx; - conn = data->conn; - if(!conn) { - /* Unhappy hack with lifetimes of transfers and connection */ - if(!data->set.connect_only) { - failf(data, "[WS] CONNECT_ONLY is required"); - return CURLE_UNSUPPORTED_PROTOCOL; + if(buflen && !buffer) { + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; } - Curl_getconnectinfo(data, &conn); + conn = data->conn; if(!conn) { - failf(data, "[WS] connection not found"); - return CURLE_BAD_FUNCTION_ARGUMENT; - } - } - ws = Curl_conn_meta_get(conn, CURL_META_PROTO_WS_CONN); - if(!ws) { - failf(data, "[WS] connection is not setup for websocket"); - return CURLE_BAD_FUNCTION_ARGUMENT; - } - - memset(&ctx, 0, sizeof(ctx)); - ctx.data = data; - ctx.ws = ws; - ctx.buffer = buffer; - ctx.buflen = buflen; - - while(1) { - CURLcode result; - - /* receive more when our buffer is empty */ - if(Curl_bufq_is_empty(&ws->recvbuf)) { - size_t n; - result = Curl_bufq_slurp(&ws->recvbuf, nw_in_recv, data, &n); - if(result) - return result; - else if(n == 0) { - /* connection closed */ - infof(data, "[WS] connection expectedly closed?"); - return CURLE_GOT_NOTHING; + /* Unhappy hack with lifetimes of transfers and connection */ + if(!data->set.connect_only) { + failf(data, "[WS] CONNECT_ONLY is required"); + result = CURLE_UNSUPPORTED_PROTOCOL; + goto out; } - CURL_TRC_WS(data, "curl_ws_recv, added %zu bytes from network", - Curl_bufq_len(&ws->recvbuf)); - } - result = ws_dec_pass(&ws->dec, data, &ws->recvbuf, - ws_client_collect, &ctx); - if(result == CURLE_AGAIN) { - if(!ctx.written) { - ws_dec_info(&ws->dec, data, "need more input"); - continue; /* nothing written, try more input */ + Curl_getconnectinfo(data, &conn); + if(!conn) { + failf(data, "[WS] connection not found"); + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; } - break; } - else if(result) { - return result; + ws = Curl_conn_meta_get(conn, CURL_META_PROTO_WS_CONN); + if(!ws) { + failf(data, "[WS] connection is not setup for websocket"); + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; } - else if(ctx.written) { - /* The decoded frame is passed back to our caller. - * There are frames like PING were we auto-respond to and - * that we do not return. For these `ctx.written` is not set. */ - break; - } - } - /* update frame information to be passed back */ - update_meta(ws, ctx.frame_age, ctx.frame_flags, ctx.payload_offset, - ctx.payload_len, ctx.bufidx); - *metap = &ws->recvframe; - *recv = ws->recvframe.len; - CURL_TRC_WS(data, "curl_ws_recv(len=%zu) -> %zu bytes (frame at %" - FMT_OFF_T ", %" FMT_OFF_T " left)", - buflen, *recv, ws->recvframe.offset, - ws->recvframe.bytesleft); - /* all's well, try to send any pending control. we do not know - * when the application will call `curl_ws_send()` again. */ - if(!data->set.ws_raw_mode && ws->pending.type) { - CURLcode r2 = ws_enc_add_pending(data, ws); - if(!r2) - (void)ws_flush(data, ws, Curl_is_in_callback(data)); + memset(&ctx, 0, sizeof(ctx)); + ctx.data = data; + ctx.ws = ws; + ctx.buffer = buffer; + ctx.buflen = buflen; + + while(1) { + /* receive more when our buffer is empty */ + if(Curl_bufq_is_empty(&ws->recvbuf)) { + size_t n; + result = Curl_bufq_slurp(&ws->recvbuf, nw_in_recv, data, &n); + if(result) + goto out; + else if(n == 0) { + /* connection closed */ + infof(data, "[WS] connection expectedly closed?"); + result = CURLE_GOT_NOTHING; + goto out; + } + CURL_TRC_WS(data, "curl_ws_recv, added %zu bytes from network", + Curl_bufq_len(&ws->recvbuf)); + } + + result = ws_dec_pass(&ws->dec, data, &ws->recvbuf, + ws_client_collect, &ctx); + if(result == CURLE_AGAIN) { + if(!ctx.written) { + ws_dec_info(&ws->dec, data, "need more input"); + continue; /* nothing written, try more input */ + } + break; + } + else if(result) { + goto out; + } + else if(ctx.written) { + /* The decoded frame is passed back to our caller. + * There are frames like PING were we auto-respond to and + * that we do not return. For these `ctx.written` is not set. */ + break; + } + } + + /* update frame information to be passed back */ + update_meta(ws, ctx.frame_age, ctx.frame_flags, ctx.payload_offset, + ctx.payload_len, ctx.bufidx); + *metap = &ws->recvframe; + *recv = ws->recvframe.len; + CURL_TRC_WS(data, "curl_ws_recv(len=%zu) -> %zu bytes (frame at %" + FMT_OFF_T ", %" FMT_OFF_T " left)", + buflen, *recv, ws->recvframe.offset, + ws->recvframe.bytesleft); + /* all's well, try to send any pending control. we do not know + * when the application will call `curl_ws_send()` again. */ + if(!data->set.ws_raw_mode && ws->pending.type) { + CURLcode r2 = ws_enc_add_pending(data, ws); + if(!r2) + (void)ws_flush(data, ws, Curl_api_is_in_callback(data)); + } + result = CURLE_OK; } - return CURLE_OK; +out: + CURL_EAPI_LEAVE(&guard); + return result; } static CURLcode ws_flush(struct Curl_easy *data, struct websocket *ws, @@ -1721,7 +1733,7 @@ static CURLcode ws_flush(struct Curl_easy *data, struct websocket *ws, result = ws_send_raw_blocking(data, ws, (const char *)out, outlen); n = result ? 0 : outlen; } - else if(data->set.connect_only || Curl_is_in_callback(data)) + else if(data->set.connect_only || Curl_api_is_in_callback(data)) result = Curl_senddata(data, out, outlen, &n); else { result = Curl_xfer_send(data, out, outlen, FALSE, &n); @@ -1805,7 +1817,7 @@ static CURLcode ws_send_raw(struct Curl_easy *data, const void *buffer, if(!buflen) return CURLE_OK; - if(Curl_is_in_callback(data)) { + if(Curl_api_is_in_callback(data)) { /* When invoked from inside callbacks, we do a blocking send as the * callback will probably not implement partial writes that may then * mess up the ws framing subsequently. @@ -1835,75 +1847,80 @@ CURLcode curl_ws_send(CURL *curl, const void *buffer_arg, curl_off_t fragsize, unsigned int flags) { - struct websocket *ws; - const uint8_t *buffer = buffer_arg; + struct Curl_eapi_guard guard; CURLcode result = CURLE_OK; - struct Curl_easy *data = curl; - size_t ndummy; - size_t *pnsent = sent ? sent : &ndummy; - if(!GOOD_EASY_HANDLE(data)) - return CURLE_BAD_FUNCTION_ARGUMENT; - CURL_TRC_WS(data, "curl_ws_send(len=%zu, fragsize=%" FMT_OFF_T - ", flags=%x), raw=%d", - buflen, fragsize, flags, data->set.ws_raw_mode); + if(CURL_EAPI_ENTER(&guard, curl, ws_send, &result)) { + struct websocket *ws; + const uint8_t *buffer = buffer_arg; + struct Curl_easy *data = curl; + size_t ndummy; + size_t *pnsent = sent ? sent : &ndummy; - *pnsent = 0; + CURL_TRC_WS(data, "curl_ws_send(len=%zu, fragsize=%" FMT_OFF_T + ", flags=%x), raw=%d", + buflen, fragsize, flags, data->set.ws_raw_mode); - if(!buffer && buflen) { - failf(data, "[WS] buffer is NULL when buflen is not"); - result = CURLE_BAD_FUNCTION_ARGUMENT; - goto out; - } + *pnsent = 0; - if(!data->conn && data->set.connect_only) { - result = Curl_connect_only_attach(data); - if(result) + if(!buffer && buflen) { + failf(data, "[WS] buffer is NULL when buflen is not"); + result = CURLE_BAD_FUNCTION_ARGUMENT; goto out; - } - if(!data->conn) { - failf(data, "[WS] No associated connection"); - result = CURLE_SEND_ERROR; - goto out; - } - ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN); - if(!ws) { - failf(data, "[WS] Not a websocket transfer"); - result = CURLE_SEND_ERROR; - goto out; - } + } - if(data->set.ws_raw_mode) { - /* In raw mode, we write directly to the connection */ - /* try flushing any content still waiting to be sent. */ - result = ws_flush(data, ws, FALSE); - if(result) + if(!data->conn && data->set.connect_only) { + result = Curl_connect_only_attach(data); + if(result) + goto out; + } + if(!data->conn) { + failf(data, "[WS] No associated connection"); + result = CURLE_SEND_ERROR; goto out; + } + ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN); + if(!ws) { + failf(data, "[WS] Not a websocket transfer"); + result = CURLE_SEND_ERROR; + goto out; + } - if(!buffer) { - failf(data, "[WS] buffer is NULL in raw mode"); - return CURLE_BAD_FUNCTION_ARGUMENT; + if(data->set.ws_raw_mode) { + /* In raw mode, we write directly to the connection */ + /* try flushing any content still waiting to be sent. */ + result = ws_flush(data, ws, FALSE); + if(result) + goto out; + + if(!buffer) { + failf(data, "[WS] buffer is NULL in raw mode"); + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; + } + if(!sent) { + failf(data, "[WS] sent is NULL in raw mode"); + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; + } + if(fragsize || flags) { + failf(data, "[WS] fragsize and flags must be zero in raw mode"); + result = CURLE_BAD_FUNCTION_ARGUMENT; + goto out; + } + result = ws_send_raw(data, buffer, buflen, pnsent); + goto out; } - if(!sent) { - failf(data, "[WS] sent is NULL in raw mode"); - return CURLE_BAD_FUNCTION_ARGUMENT; - } - if(fragsize || flags) { - failf(data, "[WS] fragsize and flags must be zero in raw mode"); - return CURLE_BAD_FUNCTION_ARGUMENT; - } - result = ws_send_raw(data, buffer, buflen, pnsent); - goto out; + + /* Not RAW mode, we do the frame encoding */ + result = ws_enc_send(data, ws, buffer, buflen, fragsize, flags, pnsent); + CURL_TRC_WS(data, "curl_ws_send(len=%zu, fragsize=%" FMT_OFF_T + ", flags=%x, raw=%d) -> %d, %zu", + buflen, fragsize, flags, data->set.ws_raw_mode, (int)result, + *pnsent); } - - /* Not RAW mode, we do the frame encoding */ - result = ws_enc_send(data, ws, buffer, buflen, fragsize, flags, pnsent); - out: - CURL_TRC_WS(data, "curl_ws_send(len=%zu, fragsize=%" FMT_OFF_T - ", flags=%x, raw=%d) -> %d, %zu", - buflen, fragsize, flags, data->set.ws_raw_mode, (int)result, - *pnsent); + CURL_EAPI_LEAVE(&guard); return result; } @@ -1923,7 +1940,7 @@ const struct curl_ws_frame *curl_ws_meta(CURL *curl) /* we only return something for websocket, called from within the callback when not using raw mode */ struct Curl_easy *data = curl; - if(GOOD_EASY_HANDLE(data) && Curl_is_in_callback(data) && + if(GOOD_EASY_HANDLE(data) && Curl_api_is_in_callback(data) && data->conn && !data->set.ws_raw_mode) { struct websocket *ws; ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN); @@ -1937,46 +1954,48 @@ CURL_EXTERN CURLcode curl_ws_start_frame(CURL *curl, unsigned int flags, curl_off_t frame_len) { - struct websocket *ws; + struct Curl_eapi_guard guard; CURLcode result = CURLE_OK; - struct Curl_easy *data = curl; - if(!GOOD_EASY_HANDLE(data)) - return CURLE_BAD_FUNCTION_ARGUMENT; + if(CURL_EAPI_ENTER(&guard, curl, ws_start_frame, &result)) { + struct Curl_easy *data = curl; + struct websocket *ws; - if(data->set.ws_raw_mode) { - failf(data, "cannot curl_ws_start_frame() with CURLWS_RAW_MODE enabled"); - return CURLE_FAILED_INIT; + if(data->set.ws_raw_mode) { + failf(data, "cannot curl_ws_start_frame() with CURLWS_RAW_MODE enabled"); + result = CURLE_FAILED_INIT; + goto out; + } + + CURL_TRC_WS(data, "curl_ws_start_frame(flags=%x, frame_len=%" FMT_OFF_T, + flags, frame_len); + + if(!data->conn) { + failf(data, "[WS] No associated connection"); + result = CURLE_SEND_ERROR; + goto out; + } + ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN); + if(!ws) { + failf(data, "[WS] Not a websocket transfer"); + result = CURLE_SEND_ERROR; + goto out; + } + + if(ws->enc.payload_remain) { + failf(data, "[WS] previous frame not finished"); + result = CURLE_SEND_ERROR; + goto out; + } + + result = ws_enc_write_head(data, ws, &ws->enc, flags, frame_len, + &ws->sendbuf); + if(result) + CURL_TRC_WS(data, "curl_start_frame(), error adding frame head %d", + (int)result); } - - CURL_TRC_WS(data, "curl_ws_start_frame(flags=%x, frame_len=%" FMT_OFF_T, - flags, frame_len); - - if(!data->conn) { - failf(data, "[WS] No associated connection"); - result = CURLE_SEND_ERROR; - goto out; - } - ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN); - if(!ws) { - failf(data, "[WS] Not a websocket transfer"); - result = CURLE_SEND_ERROR; - goto out; - } - - if(ws->enc.payload_remain) { - failf(data, "[WS] previous frame not finished"); - result = CURLE_SEND_ERROR; - goto out; - } - - result = ws_enc_write_head(data, ws, &ws->enc, flags, frame_len, - &ws->sendbuf); - if(result) - CURL_TRC_WS(data, "curl_start_frame(), error adding frame head %d", - (int)result); - out: + CURL_EAPI_LEAVE(&guard); return result; } diff --git a/tests/unit/unit3214.c b/tests/unit/unit3214.c index 191ef2f3f6..2697886931 100644 --- a/tests/unit/unit3214.c +++ b/tests/unit/unit3214.c @@ -48,7 +48,7 @@ static void checksize(const char *name, size_t size, size_t allowed) These sizes were chosen with platforms with 64-bit pointers in mind. */ #define MAX_CURL_EASY 5370 #define MAX_CONNECTDATA 1300 -#define MAX_CURL_MULTI 850 +#define MAX_CURL_MULTI 920 #define MAX_CURL_HTTPPOST 112 #define MAX_CURL_SLIST 16 #define MAX_CURL_KHKEY 24