From dfc01ea2a3e01f04a310456af2132e2fc001f933 Mon Sep 17 00:00:00 2001 From: Stefan Eissing Date: Mon, 6 Jul 2026 12:59:45 +0200 Subject: [PATCH] hardening: add API guards Add call stacks to easy and multi instances. Record ongoing API calls and callback invocations there to detect recursion and not allowed invocations. Define enums for easy, multi and callbacks in `api.h`. In `api.c` define properties for these functions: - can they recurse - is the easy/multi handle destroyed during the call or should it be good afterwards - is the call allowed when a multi event callback is ongoing - is the call allowed when a notification callback is ongoing Entering a guard - checks that passed CURL*/CURLM* are GOOD on entering - checks that easy handle's `mid` is correct and it is known for it in the multi. - checks that call properties are obeyed (recursion, callback checks) - checks that passed CURL*/CURLM* are GOOD on leaving, unless call is known to kill it Checks for ongoing callbacks inspect the whole call stack and catches nested invocations (which our current flags can not). Call stacks in easy/multi handle are fixed size and will deny recursion when the limit is reached. The current limits are 7 for easy and 15 for multi now. Removes: - multi->in_callback, check is done via call stack - multi->in_ntfy_cb, check is done via call stack The overhead in my tests seems minimal, if noticeable at all. Closes #22237 --- lib/Makefile.inc | 2 + lib/api.c | 398 ++++++++++++++++++++ lib/api.h | 207 +++++++++++ lib/cf-socket.c | 20 +- lib/curl_trc.c | 17 +- lib/cw-out.c | 9 +- lib/doh.c | 4 +- lib/easy.c | 460 ++++++++++++----------- lib/ftp.c | 20 +- lib/ftplistparser.c | 21 +- lib/hostip.c | 5 +- lib/http2.c | 13 +- lib/http_chunks.c | 9 +- lib/multi.c | 825 ++++++++++++++++++++++-------------------- lib/multi_ev.c | 22 +- lib/multi_ntfy.c | 12 +- lib/multihandle.h | 25 +- lib/multiif.h | 12 +- lib/progress.c | 10 +- lib/rtsp.c | 5 +- lib/sendf.c | 25 +- lib/setopt.c | 21 +- lib/transfer.h | 4 + lib/url.c | 2 +- lib/urldata.h | 53 ++- lib/vssh/libssh.c | 11 +- lib/vssh/libssh2.c | 20 +- lib/vtls/openssl.c | 5 +- lib/ws.c | 371 ++++++++++--------- tests/unit/unit3214.c | 2 +- 30 files changed, 1667 insertions(+), 943 deletions(-) create mode 100644 lib/api.c create mode 100644 lib/api.h 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