hardening: add API guards
No binary (build failed)
—
(log yok)
| @@ -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 \ | |
| @@ -0,0 +1,398 @@ | |
| + | /*************************************************************************** |
| + | * _ _ ____ _ |
| + | * Project ___| | | | _ \| | |
| + | * / __| | | | |_) | | |
| + | * | (__| |_| | _ <| |___ |
| + | * \___|\___/|_| \_\_____| |
| + | * |
| + | * Copyright (C) Daniel Stenberg, <[email protected]>, 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); |
| + | } |
| @@ -0,0 +1,207 @@ | |
| + | #ifndef HEADER_CURL_API_H |
| + | #define HEADER_CURL_API_H |
| + | /*************************************************************************** |
| + | * _ _ ____ _ |
| + | * Project ___| | | | _ \| | |
| + | * / __| | | | |_) | | |
| + | * | (__| |_| | _ <| |___ |
| + | * \___|\___/|_| \_\_____| |
| + | * |
| + | * Copyright (C) Daniel Stenberg, <[email protected]>, 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 */ |
| @@ -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; | |
| @@ -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 { | |
| @@ -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); | |
| @@ -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; | |
| @@ -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; |
| + | |
| + | outcurl = curlx_calloc(1, sizeof(struct Curl_easy)); |
| + | if(!outcurl) |
| + | goto fail; |
| - | /* |
| - | * We setup a few buffers we need. We should probably make them |
| - | * get setup on-demand in the code, as that would probably decrease |
| - | * the likeliness of us forgetting to init a buffer here in the future. |
| - | */ |
| - | outcurl->set.buffer_size = data->set.buffer_size; |
| - | |
| - | 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; |
| + | /* |
| + | * 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; |
| + | |
| + | 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); |
| - | |
| - | /* Copy entries learned at runtime. (E.g. Strict-Transport-Security |
| - | headers.) */ |
| - | if(Curl_hsts_copy(outcurl->hsts, data->hsts)) |
| - | goto fail; |
| - | } |
| + | 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; |
| + | } |
| #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; |
| - | |
| - | in_c = Curl_is_in_callback(data); |
| - | if(in_c == IN_CALLBACK_FORBID_EASY_PAUSE) |
| - | return CURLE_RECURSIVE_API_CALL; |
| - | 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(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; |
| - | 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(!data->conn) { |
| + | /* crazy input, do not continue */ |
| + | result = CURLE_BAD_FUNCTION_ARGUMENT; |
| + | goto out; |
| + | } |
| - | if(recv_paused != recv_paused_new) { |
| - | changed = TRUE; |
| - | result = Curl_1st_fatal( |
| - | result, Curl_xfer_pause_recv(data, recv_paused_new)); |
| - | } |
| + | 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 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((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(!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(recv_paused != recv_paused_new) { |
| + | changed = TRUE; |
| + | result = Curl_1st_fatal( |
| + | result, Curl_xfer_pause_recv(data, recv_paused_new)); |
| + | } |
| - | 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); |
| + | /* 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; |
| + | } |
| + | 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; |
| - | |
| - | if(Curl_is_in_callback(data)) |
| - | return CURLE_RECURSIVE_API_CALL; |
| + | struct Curl_eapi_guard guard; |
| + | CURLcode result; |
| - | /* 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; | |
| @@ -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) ? | |
| @@ -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); | |
| @@ -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; | |
| @@ -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); | |
| @@ -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"); | |
| @@ -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; |
| - | |
| - | if(!GOOD_MULTI_HANDLE(multi)) |
| - | return CURLM_BAD_HANDLE; |
| - | |
| - | if(multi->in_callback) |
| - | return CURLM_RECURSIVE_API_CALL; |
| + | struct Curl_mapi_guard guard; |
| + | CURLMcode mresult; |
| - | Curl_pollset_init(&ps); |
| - | if(Curl_uint32_bset_first(&multi->process, &mid)) { |
| - | do { |
| - | struct Curl_easy *data = Curl_multi_get_easy(multi, mid); |
| + | 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(!data) { |
| - | DEBUGASSERT(0); |
| - | continue; |
| - | } |
| + | Curl_pollset_init(&ps); |
| + | if(Curl_uint32_bset_first(&multi->process, &mid)) { |
| + | do { |
| + | struct Curl_easy *data = Curl_multi_get_easy(multi, mid); |
| - | 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_cshutdn_setfds(&multi->cshutdn, multi->admin, |
| - | read_fd_set, write_fd_set, &this_max_fd); |
| + | 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)); |
| + | } |
| - | *max_fd = this_max_fd; |
| - | Curl_pollset_cleanup(&ps); |
| + | Curl_cshutdn_setfds(&multi->cshutdn, multi->admin, |
| + | read_fd_set, write_fd_set, &this_max_fd); |
| - | return CURLM_OK; |
| + | *max_fd = this_max_fd; |
| + | Curl_pollset_cleanup(&ps); |
| + | |
| + | mresult = 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; |
| - | |
| - | if(!ufds && (size || !fd_count)) |
| - | return CURLM_BAD_FUNCTION_ARGUMENT; |
| + | struct Curl_mapi_guard guard; |
| + | CURLMcode mresult; |
| - | if(!GOOD_MULTI_HANDLE(multi)) |
| - | return CURLM_BAD_HANDLE; |
| + | 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(multi->in_callback) |
| - | return CURLM_RECURSIVE_API_CALL; |
| + | if(!ufds && (size || !fd_count)) { |
| + | mresult = CURLM_BAD_FUNCTION_ARGUMENT; |
| + | goto out; |
| + | } |
| - | 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)); |
| - | } |
| + | 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)); |
| + | } |
| - | need += Curl_cshutdn_add_waitfds(&multi->cshutdn, multi->admin, &cwfds); |
| + | need += Curl_cshutdn_add_waitfds(&multi->cshutdn, multi->admin, &cwfds); |
| - | if(need != cwfds.n && ufds) |
| - | mresult = CURLM_OUT_OF_MEMORY; |
| + | if(need != cwfds.n && ufds) |
| + | mresult = CURLM_OUT_OF_MEMORY; |
| - | if(fd_count) |
| - | *fd_count = need; |
| - | Curl_pollset_cleanup(&ps); |
| + | 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; |
| - | |
| - | if(!GOOD_MULTI_HANDLE(multi)) |
| - | return CURLM_BAD_HANDLE; |
| + | struct Curl_mapi_guard guard; |
| + | CURLMcode mresult; |
| - | 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); |
| + | 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; |
| } | |
| - | if(val & CURLMNWC_CLEAR_CONNS) { |
| - | Curl_cpool_nw_changed(multi->admin); |
| + | 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; |
| } | |
| - | 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: |
| + | 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: |
| + | uarg = va_arg(param, long); |
| + | if((uarg <= 0) || (uarg > UINT32_MAX)) |
| mresult = CURLM_BAD_FUNCTION_ARGUMENT; | |
| - | break; |
| - | case CURLE_OUT_OF_MEMORY: |
| - | mresult = CURLM_OUT_OF_MEMORY; |
| - | break; |
| - | default: |
| - | mresult = CURLM_INTERNAL_ERROR; |
| - | break; |
| + | 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; |
| - | |
| - | /* First, make some basic checks that the CURLM handle is a good handle */ |
| - | if(!GOOD_MULTI_HANDLE(multi)) |
| - | return CURLM_BAD_HANDLE; |
| + | struct Curl_mapi_guard guard; |
| + | CURLMcode mresult; |
| - | if(multi->in_callback) |
| - | return CURLM_RECURSIVE_API_CALL; |
| + | if(CURL_MAPI_ENTER(&guard, m, multi_timeout, &mresult)) { |
| + | struct curltime expire_time; |
| - | 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 | |
| @@ -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; | |
| @@ -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; | |
| @@ -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 | |
| @@ -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); | |
| @@ -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"); | |
| @@ -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"); | |
| @@ -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); | |
| @@ -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; | |
| } | |
| @@ -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 */ | |
| @@ -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 | |
| @@ -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,34 +1212,35 @@ 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 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, |
| - | so it has no uniqueness guarantee for large processings. Note: it has no |
| - | 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 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_share *share; /* Share, handles global variable mutexing */ |
| + | 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, |
| + | so it has no uniqueness guarantee for large processings. Note: it has no |
| + | uniqueness either IFF more than one connection pool is used by the |
| + | libcurl application. */ |
| + | curl_off_t id; |
| + | uint32_t master_mid; /* if set, this transfer belongs to a master */ |
| + | multi_sub_xfer_done_cb *sub_xfer_done; |
| /* `meta_hash` is a general key-value store for implementations | |
| * with the lifetime of the easy handle. | |
| @@ -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) { | |
| @@ -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)) { | |
| @@ -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; | |
| @@ -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; |
| + | |
| + | 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_is_in_callback(data)); |
| + | /* 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); |
| - | *pnsent = 0; |
| + | 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; |
| - | if(!buffer && buflen) { |
| - | failf(data, "[WS] buffer is NULL when buflen is not"); |
| - | result = CURLE_BAD_FUNCTION_ARGUMENT; |
| - | goto out; |
| - | } |
| + | 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(!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; |
| - | } |
| + | *pnsent = 0; |
| - | 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(!buffer && buflen) { |
| + | failf(data, "[WS] buffer is NULL when buflen is not"); |
| + | result = CURLE_BAD_FUNCTION_ARGUMENT; |
| goto out; | |
| + | } |
| - | if(!buffer) { |
| - | failf(data, "[WS] buffer is NULL in raw mode"); |
| - | return CURLE_BAD_FUNCTION_ARGUMENT; |
| + | if(!data->conn && data->set.connect_only) { |
| + | result = Curl_connect_only_attach(data); |
| + | if(result) |
| + | goto out; |
| } | |
| - | if(!sent) { |
| - | failf(data, "[WS] sent is NULL in raw mode"); |
| - | return CURLE_BAD_FUNCTION_ARGUMENT; |
| + | if(!data->conn) { |
| + | failf(data, "[WS] No associated connection"); |
| + | result = CURLE_SEND_ERROR; |
| + | goto out; |
| } | |
| - | if(fragsize || flags) { |
| - | failf(data, "[WS] fragsize and flags must be zero in raw mode"); |
| - | return CURLE_BAD_FUNCTION_ARGUMENT; |
| + | 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; |
| } | |
| - | 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); |
| + | 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; |
| + | } |
| + | /* 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); |
| + | } |
| 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(data->set.ws_raw_mode) { |
| - | failf(data, "cannot curl_ws_start_frame() with CURLWS_RAW_MODE enabled"); |
| - | return CURLE_FAILED_INIT; |
| - | } |
| + | if(CURL_EAPI_ENTER(&guard, curl, ws_start_frame, &result)) { |
| + | struct Curl_easy *data = curl; |
| + | struct websocket *ws; |
| - | CURL_TRC_WS(data, "curl_ws_start_frame(flags=%x, frame_len=%" FMT_OFF_T, |
| - | flags, frame_len); |
| + | if(data->set.ws_raw_mode) { |
| + | failf(data, "cannot curl_ws_start_frame() with CURLWS_RAW_MODE enabled"); |
| + | result = CURLE_FAILED_INIT; |
| + | 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; |
| - | } |
| + | CURL_TRC_WS(data, "curl_ws_start_frame(flags=%x, frame_len=%" FMT_OFF_T, |
| + | flags, frame_len); |
| - | if(ws->enc.payload_remain) { |
| - | failf(data, "[WS] previous frame not finished"); |
| - | result = CURLE_SEND_ERROR; |
| - | 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; |
| + | } |
| - | 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); |
| + | 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; | |
| } | |
| @@ -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 | |