mirror of
https://github.com/curl/curl
synced 2026-08-25 12:32:35 -04:00
Add API guards for curl_easy_header and curl_easy_nextheader. urldata.h: remove prevhead member as not used Closes #22530
451 lines
15 KiB
C
451 lines
15 KiB
C
/***************************************************************************
|
|
* _ _ ____ _
|
|
* Project ___| | | | _ \| |
|
|
* / __| | | | |_) | |
|
|
* | (__| |_| | _ <| |___
|
|
* \___|\___/|_| \_\_____|
|
|
*
|
|
* Copyright (C) Daniel Stenberg, <daniel@haxx.se>, 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 "curl_threads.h"
|
|
#include "multiif.h"
|
|
#include "vtls/vtls_scache.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 */
|
|
uint8_t no_scache_lock; /* may not be called with easy's vtls_scache
|
|
locked by current thread */
|
|
};
|
|
|
|
static const struct Curl_eapi_fn_props eapi_fn_props[CURL_EAPI_FN_LAST] = {
|
|
/* function kill rec !ev !scach */
|
|
{ CURL_EAPI_FN_easy_cleanup, 1, 0, 0, 0 },
|
|
{ CURL_EAPI_FN_easy_duphandle, 0, 1, 0, 0 },
|
|
{ CURL_EAPI_FN_easy_getinfo, 0, 1, 0, 0 },
|
|
{ CURL_EAPI_FN_easy_header, 0, 1, 0, 0 },
|
|
{ CURL_EAPI_FN_easy_nextheader, 0, 1, 0, 0 },
|
|
{ CURL_EAPI_FN_easy_pause, 0, 1, 1, 0 },
|
|
{ CURL_EAPI_FN_easy_perform_ev, 0, 0, 0, 1 },
|
|
{ CURL_EAPI_FN_easy_perform, 0, 0, 0, 1 },
|
|
{ CURL_EAPI_FN_easy_recv, 0, 0, 0, 1 },
|
|
{ CURL_EAPI_FN_easy_reset, 0, 0, 0, 0 },
|
|
{ CURL_EAPI_FN_easy_send, 0, 0, 0, 1 },
|
|
{ CURL_EAPI_FN_easy_setopt, 0, 1, 0, 0 },
|
|
{ CURL_EAPI_FN_easy_ssls_export, 0, 0, 0, 1 },
|
|
{ CURL_EAPI_FN_easy_ssls_import, 0, 0, 0, 1 },
|
|
{ CURL_EAPI_FN_easy_upkeep, 0, 0, 0, 1 },
|
|
{ CURL_EAPI_FN_ws_recv, 0, 1, 0, 1 },
|
|
{ CURL_EAPI_FN_ws_send, 0, 1, 0, 1 },
|
|
{ CURL_EAPI_FN_ws_start_frame, 0, 1, 0, 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 */
|
|
uint8_t no_scache_lock; /* may not be called with multi's vtls_scache
|
|
locked by current thread */
|
|
};
|
|
|
|
static const struct Curl_mapi_fn_props mapi_fn_props[CURL_MAPI_FN_LAST] = {
|
|
/* function kill rec ntfy !scach */
|
|
{ CURL_MAPI_FN_multi_add_handle, 0, 0, 1, 0 },
|
|
{ CURL_MAPI_FN_multi_assign, 0, 1, 1, 0 },
|
|
{ CURL_MAPI_FN_multi_cleanup, 1, 0, 0, 1 },
|
|
{ CURL_MAPI_FN_multi_fdset, 0, 0, 1, 0 },
|
|
{ CURL_MAPI_FN_multi_get_handles, 0, 1, 1, 0 },
|
|
{ CURL_MAPI_FN_multi_get_offt, 0, 1, 1, 0 },
|
|
{ CURL_MAPI_FN_multi_info_read, 0, 1, 1, 0 },
|
|
{ CURL_MAPI_FN_multi_notify_disable, 0, 1, 1, 0 },
|
|
{ CURL_MAPI_FN_multi_notify_enable, 0, 1, 1, 0 },
|
|
{ CURL_MAPI_FN_multi_perform, 0, 0, 0, 1 },
|
|
{ CURL_MAPI_FN_multi_poll, 0, 0, 1, 0 },
|
|
{ CURL_MAPI_FN_multi_remove_handle, 0, 0, 1, 0 },
|
|
{ CURL_MAPI_FN_multi_setopt, 0, 0, 1, 0 },
|
|
{ CURL_MAPI_FN_multi_socket_action, 0, 0, 0, 1 },
|
|
{ CURL_MAPI_FN_multi_socket_all, 0, 0, 0, 1 },
|
|
{ CURL_MAPI_FN_multi_socket, 0, 0, 0, 1 },
|
|
{ CURL_MAPI_FN_multi_timeout, 0, 0, 1, 1 },
|
|
{ CURL_MAPI_FN_multi_wait, 0, 0, 1, 0 },
|
|
{ CURL_MAPI_FN_multi_waitfds, 0, 0, 1, 0 },
|
|
};
|
|
|
|
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 %hu 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 %hu 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 %hu while event callback ongoing\n",
|
|
(uint16_t)fn));
|
|
#endif
|
|
result = CURLE_RECURSIVE_API_CALL;
|
|
goto out;
|
|
}
|
|
|
|
#if defined(USE_SSL) && defined(USE_MUTEX)
|
|
if(fn_props->no_scache_lock &&
|
|
Curl_ssl_scache_is_locked_by_current_thread(data)) {
|
|
#ifdef CURLVERBOSE
|
|
DEBUGF(curl_mfprintf(stderr,
|
|
"EAPI guard: calling %hu while vtls_scache is locked by "
|
|
"current thread\n", (uint16_t)fn));
|
|
#endif
|
|
result = CURLE_RECURSIVE_API_CALL;
|
|
goto out;
|
|
}
|
|
#endif
|
|
|
|
/* 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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
CURLHcode Curl_eapi_hcode(CURLcode result)
|
|
{
|
|
switch(result) {
|
|
case CURLE_OK:
|
|
return CURLHE_OK;
|
|
case CURLE_BAD_FUNCTION_ARGUMENT:
|
|
return CURLHE_BAD_ARGUMENT;
|
|
case CURLE_OUT_OF_MEMORY:
|
|
return CURLHE_OUT_OF_MEMORY;
|
|
case CURLE_NOT_BUILT_IN:
|
|
return CURLHE_NOT_BUILT_IN;
|
|
default:
|
|
/* Unfortunately, we cannot convert RECURSIVE_API_CALL,
|
|
* but since the header API is reentrant, this should not happen. */
|
|
return CURLHE_BAD_ARGUMENT;
|
|
}
|
|
}
|
|
|
|
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 %hu with call to %u ongoing\n", (uint16_t)fn,
|
|
multi->callstack.calls[multi->callstack.count-1]));
|
|
#endif
|
|
mresult = CURLM_RECURSIVE_API_CALL;
|
|
goto out;
|
|
}
|
|
|
|
#if defined(USE_SSL) && defined(USE_MUTEX)
|
|
if(fn_props->no_scache_lock && multi->ssl_scache &&
|
|
Curl_ssl_scache_is_locked_by_current_thread(multi->admin)) {
|
|
#ifdef CURLVERBOSE
|
|
DEBUGF(curl_mfprintf(stderr,
|
|
"MAPI guard: calling %hu while its vtls_scache is locked by "
|
|
"current thread\n", (uint16_t)fn));
|
|
#endif
|
|
mresult = CURLM_RECURSIVE_API_CALL;
|
|
goto out;
|
|
}
|
|
#endif
|
|
|
|
/* 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);
|
|
}
|