mirror of
https://github.com/curl/curl.git
synced 2026-07-22 23:27:15 +03:00
hardening: add API guards
Add call stacks to easy and multi instances. Record ongoing API calls and callback invocations there to detect recursion and not allowed invocations. Define enums for easy, multi and callbacks in `api.h`. In `api.c` define properties for these functions: - can they recurse - is the easy/multi handle destroyed during the call or should it be good afterwards - is the call allowed when a multi event callback is ongoing - is the call allowed when a notification callback is ongoing Entering a guard - checks that passed CURL*/CURLM* are GOOD on entering - checks that easy handle's `mid` is correct and it is known for it in the multi. - checks that call properties are obeyed (recursion, callback checks) - checks that passed CURL*/CURLM* are GOOD on leaving, unless call is known to kill it Checks for ongoing callbacks inspect the whole call stack and catches nested invocations (which our current flags can not). Call stacks in easy/multi handle are fixed size and will deny recursion when the limit is reached. The current limits are 7 for easy and 15 for multi now. Removes: - multi->in_callback, check is done via call stack - multi->in_ntfy_cb, check is done via call stack The overhead in my tests seems minimal, if noticeable at all. Closes #22237
This commit is contained in:
parent
ab8d771d31
commit
dfc01ea2a3
30 changed files with 1667 additions and 943 deletions
|
|
@ -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 \
|
||||
|
|
|
|||
398
lib/api.c
Normal file
398
lib/api.c
Normal file
|
|
@ -0,0 +1,398 @@
|
|||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* 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 "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);
|
||||
}
|
||||
207
lib/api.h
Normal file
207
lib/api.h
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
#ifndef HEADER_CURL_API_H
|
||||
#define HEADER_CURL_API_H
|
||||
/***************************************************************************
|
||||
* _ _ ____ _
|
||||
* 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"
|
||||
|
||||
#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;
|
||||
|
|
|
|||
460
lib/easy.c
460
lib/easy.c
|
|
@ -44,6 +44,7 @@
|
|||
#endif
|
||||
|
||||
#include "urldata.h"
|
||||
#include "api.h"
|
||||
#include "transfer.h"
|
||||
#include "vtls/vtls.h"
|
||||
#include "vtls/vtls_scache.h"
|
||||
|
|
@ -776,7 +777,7 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events)
|
|||
return CURLE_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
if(multi->in_callback)
|
||||
if(Curl_api_multi_is_in_callback(multi))
|
||||
return CURLE_RECURSIVE_API_CALL;
|
||||
|
||||
/* Copy relevant easy options to the multi handle */
|
||||
|
|
@ -784,7 +785,7 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events)
|
|||
curl_multi_setopt(multi, CURLMOPT_QUICK_EXIT, (long)data->set.quick_exit);
|
||||
|
||||
data->multi_easy = NULL; /* pretend it does not exist */
|
||||
mresult = curl_multi_add_handle(multi, data);
|
||||
mresult = Curl_multi_add_handle(multi, data);
|
||||
if(mresult) {
|
||||
curl_multi_cleanup(multi);
|
||||
if(mresult == CURLM_OUT_OF_MEMORY)
|
||||
|
|
@ -803,7 +804,7 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events)
|
|||
|
||||
/* ignoring the return code is not nice, but atm we cannot really handle
|
||||
a failure here, room for future improvement! */
|
||||
(void)curl_multi_remove_handle(multi, data);
|
||||
(void)Curl_multi_remove_handle(multi, data);
|
||||
|
||||
sigpipe_restore(&sigpipe_ctx);
|
||||
|
||||
|
|
@ -817,7 +818,14 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events)
|
|||
*/
|
||||
CURLcode curl_easy_perform(CURL *curl)
|
||||
{
|
||||
return easy_perform(curl, FALSE);
|
||||
struct Curl_eapi_guard guard;
|
||||
CURLcode result;
|
||||
|
||||
if(CURL_EAPI_ENTER(&guard, curl, easy_perform, &result)) {
|
||||
result = easy_perform(curl, FALSE);
|
||||
}
|
||||
CURL_EAPI_LEAVE(&guard);
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifdef DEBUGBUILD
|
||||
|
|
@ -827,7 +835,14 @@ CURLcode curl_easy_perform(CURL *curl)
|
|||
*/
|
||||
CURLcode curl_easy_perform_ev(struct Curl_easy *easy)
|
||||
{
|
||||
return easy_perform(easy, TRUE);
|
||||
struct Curl_eapi_guard guard;
|
||||
CURLcode result;
|
||||
|
||||
if(CURL_EAPI_ENTER(&guard, easy, easy_perform_ev, &result)) {
|
||||
result = easy_perform(easy, TRUE);
|
||||
}
|
||||
CURL_EAPI_LEAVE(&guard);
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
|
@ -837,13 +852,16 @@ CURLcode curl_easy_perform_ev(struct Curl_easy *easy)
|
|||
*/
|
||||
void curl_easy_cleanup(CURL *curl)
|
||||
{
|
||||
struct Curl_easy *data = curl;
|
||||
if(GOOD_EASY_HANDLE(data)) {
|
||||
struct Curl_eapi_guard guard;
|
||||
|
||||
if(CURL_EAPI_ENTER(&guard, curl, easy_cleanup, NULL)) {
|
||||
struct Curl_easy *data = curl;
|
||||
struct Curl_sigpipe_ctx sigpipe_ctx;
|
||||
sigpipe_ignore(data, &sigpipe_ctx);
|
||||
Curl_close(&data);
|
||||
sigpipe_restore(&sigpipe_ctx);
|
||||
}
|
||||
CURL_EAPI_LEAVE(&guard);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -853,20 +871,22 @@ void curl_easy_cleanup(CURL *curl)
|
|||
#undef curl_easy_getinfo
|
||||
CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...)
|
||||
{
|
||||
struct Curl_easy *data = curl;
|
||||
va_list arg;
|
||||
void *paramp;
|
||||
struct Curl_eapi_guard guard;
|
||||
CURLcode result;
|
||||
|
||||
if(!GOOD_EASY_HANDLE(data))
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
if(CURL_EAPI_ENTER(&guard, curl, easy_getinfo, &result)) {
|
||||
struct Curl_easy *data = curl;
|
||||
va_list arg;
|
||||
void *paramp;
|
||||
|
||||
va_start(arg, info);
|
||||
paramp = va_arg(arg, void *);
|
||||
va_start(arg, info);
|
||||
paramp = va_arg(arg, void *);
|
||||
|
||||
result = Curl_getinfo(data, info, paramp);
|
||||
result = Curl_getinfo(data, info, paramp);
|
||||
|
||||
va_end(arg);
|
||||
va_end(arg);
|
||||
}
|
||||
CURL_EAPI_LEAVE(&guard);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -952,122 +972,123 @@ static void dupeasy_meta_freeentry(void *p)
|
|||
*/
|
||||
CURL *curl_easy_duphandle(CURL *curl)
|
||||
{
|
||||
struct Curl_easy *data = curl;
|
||||
struct Curl_eapi_guard guard;
|
||||
struct Curl_easy *outcurl = NULL;
|
||||
|
||||
if(!GOOD_EASY_HANDLE(data))
|
||||
goto fail;
|
||||
outcurl = curlx_calloc(1, sizeof(struct Curl_easy));
|
||||
if(!outcurl)
|
||||
goto fail;
|
||||
if(CURL_EAPI_ENTER(&guard, curl, easy_duphandle, NULL)) {
|
||||
struct Curl_easy *data = curl;
|
||||
|
||||
/*
|
||||
* We setup a few buffers we need. We should probably make them
|
||||
* get setup on-demand in the code, as that would probably decrease
|
||||
* the likeliness of us forgetting to init a buffer here in the future.
|
||||
*/
|
||||
outcurl->set.buffer_size = data->set.buffer_size;
|
||||
outcurl = curlx_calloc(1, sizeof(struct Curl_easy));
|
||||
if(!outcurl)
|
||||
goto fail;
|
||||
|
||||
Curl_hash_init(&outcurl->meta_hash, 23,
|
||||
Curl_hash_str, curlx_str_key_compare, dupeasy_meta_freeentry);
|
||||
curlx_dyn_init(&outcurl->state.headerb, CURL_MAX_HTTP_HEADER);
|
||||
Curl_bufref_init(&outcurl->state.url);
|
||||
Curl_bufref_init(&outcurl->state.referer);
|
||||
Curl_netrc_init(&outcurl->state.netrc);
|
||||
/*
|
||||
* We setup a few buffers we need. We should probably make them
|
||||
* get setup on-demand in the code, as that would probably decrease
|
||||
* the likeliness of us forgetting to init a buffer here in the future.
|
||||
*/
|
||||
outcurl->set.buffer_size = data->set.buffer_size;
|
||||
|
||||
/* the connection pool is setup on demand */
|
||||
outcurl->state.lastconnect_id = -1;
|
||||
outcurl->state.recent_conn_id = -1;
|
||||
outcurl->id = -1;
|
||||
outcurl->mid = UINT32_MAX;
|
||||
outcurl->master_mid = UINT32_MAX;
|
||||
Curl_hash_init(&outcurl->meta_hash, 23,
|
||||
Curl_hash_str, curlx_str_key_compare,
|
||||
dupeasy_meta_freeentry);
|
||||
curlx_dyn_init(&outcurl->state.headerb, CURL_MAX_HTTP_HEADER);
|
||||
Curl_bufref_init(&outcurl->state.url);
|
||||
Curl_bufref_init(&outcurl->state.referer);
|
||||
Curl_netrc_init(&outcurl->state.netrc);
|
||||
|
||||
/* the connection pool is setup on demand */
|
||||
outcurl->state.lastconnect_id = -1;
|
||||
outcurl->state.recent_conn_id = -1;
|
||||
outcurl->id = -1;
|
||||
outcurl->mid = UINT32_MAX;
|
||||
outcurl->master_mid = UINT32_MAX;
|
||||
|
||||
#ifndef CURL_DISABLE_HTTP
|
||||
Curl_llist_init(&outcurl->state.httphdrs, NULL);
|
||||
Curl_llist_init(&outcurl->state.httphdrs, NULL);
|
||||
#endif
|
||||
Curl_initinfo(outcurl);
|
||||
Curl_initinfo(outcurl);
|
||||
|
||||
/* copy all userdefined values */
|
||||
if(dupset(outcurl, data))
|
||||
goto fail;
|
||||
/* copy all userdefined values */
|
||||
if(dupset(outcurl, data))
|
||||
goto fail;
|
||||
|
||||
outcurl->progress.hide = data->progress.hide;
|
||||
outcurl->progress.callback = data->progress.callback;
|
||||
outcurl->progress.hide = data->progress.hide;
|
||||
outcurl->progress.callback = data->progress.callback;
|
||||
|
||||
#ifndef CURL_DISABLE_COOKIES
|
||||
outcurl->state.cookielist = NULL;
|
||||
if(data->cookies && data->state.cookie_engine) {
|
||||
/* If cookies are enabled in the parent handle, we enable them
|
||||
in the clone as well! */
|
||||
outcurl->cookies = Curl_cookie_init();
|
||||
if(!outcurl->cookies)
|
||||
goto fail;
|
||||
outcurl->state.cookie_engine = TRUE;
|
||||
}
|
||||
outcurl->state.cookielist = NULL;
|
||||
if(data->cookies && data->state.cookie_engine) {
|
||||
/* If cookies are enabled in the parent handle, we enable them
|
||||
in the clone as well! */
|
||||
outcurl->cookies = Curl_cookie_init();
|
||||
if(!outcurl->cookies)
|
||||
goto fail;
|
||||
outcurl->state.cookie_engine = TRUE;
|
||||
}
|
||||
|
||||
if(data->state.cookielist) {
|
||||
outcurl->state.cookielist = Curl_slist_duplicate(data->state.cookielist);
|
||||
if(!outcurl->state.cookielist)
|
||||
goto fail;
|
||||
}
|
||||
if(data->state.cookielist) {
|
||||
outcurl->state.cookielist = Curl_slist_duplicate(data->state.cookielist);
|
||||
if(!outcurl->state.cookielist)
|
||||
goto fail;
|
||||
}
|
||||
#endif
|
||||
|
||||
if(Curl_bufref_ptr(&data->state.url)) {
|
||||
Curl_bufref_set(&outcurl->state.url,
|
||||
Curl_bufref_dup(&data->state.url), 0,
|
||||
curl_free);
|
||||
if(!Curl_bufref_ptr(&outcurl->state.url))
|
||||
goto fail;
|
||||
}
|
||||
if(Curl_bufref_ptr(&data->state.referer)) {
|
||||
Curl_bufref_set(&outcurl->state.referer,
|
||||
Curl_bufref_dup(&data->state.referer), 0,
|
||||
curl_free);
|
||||
if(!Curl_bufref_ptr(&outcurl->state.referer))
|
||||
goto fail;
|
||||
}
|
||||
if(Curl_bufref_ptr(&data->state.url)) {
|
||||
Curl_bufref_set(&outcurl->state.url,
|
||||
Curl_bufref_dup(&data->state.url), 0,
|
||||
curl_free);
|
||||
if(!Curl_bufref_ptr(&outcurl->state.url))
|
||||
goto fail;
|
||||
}
|
||||
if(Curl_bufref_ptr(&data->state.referer)) {
|
||||
Curl_bufref_set(&outcurl->state.referer,
|
||||
Curl_bufref_dup(&data->state.referer), 0,
|
||||
curl_free);
|
||||
if(!Curl_bufref_ptr(&outcurl->state.referer))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Reinitialize an SSL engine for the new handle
|
||||
* note: the engine name has already been copied by dupset */
|
||||
if(outcurl->set.str[STRING_SSL_ENGINE]) {
|
||||
if(Curl_ssl_set_engine(outcurl, outcurl->set.str[STRING_SSL_ENGINE]))
|
||||
goto fail;
|
||||
}
|
||||
/* Reinitialize an SSL engine for the new handle
|
||||
* note: the engine name has already been copied by dupset */
|
||||
if(outcurl->set.str[STRING_SSL_ENGINE]) {
|
||||
if(Curl_ssl_set_engine(outcurl, outcurl->set.str[STRING_SSL_ENGINE]))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
#ifndef CURL_DISABLE_ALTSVC
|
||||
if(data->asi) {
|
||||
outcurl->asi = Curl_altsvc_init();
|
||||
if(!outcurl->asi)
|
||||
goto fail;
|
||||
if(outcurl->set.str[STRING_ALTSVC])
|
||||
(void)Curl_altsvc_load(outcurl->asi, outcurl->set.str[STRING_ALTSVC]);
|
||||
}
|
||||
if(data->asi) {
|
||||
outcurl->asi = Curl_altsvc_init();
|
||||
if(!outcurl->asi)
|
||||
goto fail;
|
||||
if(outcurl->set.str[STRING_ALTSVC])
|
||||
(void)Curl_altsvc_load(outcurl->asi, outcurl->set.str[STRING_ALTSVC]);
|
||||
}
|
||||
#endif
|
||||
#ifndef CURL_DISABLE_HSTS
|
||||
if(data->hsts) {
|
||||
outcurl->hsts = Curl_hsts_init();
|
||||
if(!outcurl->hsts)
|
||||
goto fail;
|
||||
if(outcurl->set.str[STRING_HSTS])
|
||||
(void)Curl_hsts_loadfile(outcurl,
|
||||
outcurl->hsts, outcurl->set.str[STRING_HSTS]);
|
||||
(void)Curl_hsts_loadcb(outcurl, outcurl->hsts);
|
||||
if(data->hsts) {
|
||||
outcurl->hsts = Curl_hsts_init();
|
||||
if(!outcurl->hsts)
|
||||
goto fail;
|
||||
if(outcurl->set.str[STRING_HSTS])
|
||||
(void)Curl_hsts_loadfile(outcurl,
|
||||
outcurl->hsts, outcurl->set.str[STRING_HSTS]);
|
||||
(void)Curl_hsts_loadcb(outcurl, outcurl->hsts);
|
||||
|
||||
/* Copy entries learned at runtime. (E.g. Strict-Transport-Security
|
||||
headers.) */
|
||||
if(Curl_hsts_copy(outcurl->hsts, data->hsts))
|
||||
goto fail;
|
||||
}
|
||||
/* Copy entries learned at runtime. (E.g. Strict-Transport-Security
|
||||
headers.) */
|
||||
if(Curl_hsts_copy(outcurl->hsts, data->hsts))
|
||||
goto fail;
|
||||
}
|
||||
#endif
|
||||
|
||||
outcurl->magic = CURLEASY_MAGIC_NUMBER;
|
||||
|
||||
/* we reach this point and thus we are OK */
|
||||
|
||||
/* we reach this point and thus we are OK */
|
||||
outcurl->magic = CURLEASY_MAGIC_NUMBER;
|
||||
}
|
||||
CURL_EAPI_LEAVE(&guard);
|
||||
return outcurl;
|
||||
|
||||
fail:
|
||||
|
||||
if(outcurl) {
|
||||
#ifndef CURL_DISABLE_COOKIES
|
||||
curlx_free(outcurl->cookies);
|
||||
|
|
@ -1079,6 +1100,7 @@ fail:
|
|||
curlx_free(outcurl);
|
||||
}
|
||||
|
||||
CURL_EAPI_LEAVE(&guard);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
@ -1088,38 +1110,41 @@ fail:
|
|||
*/
|
||||
void curl_easy_reset(CURL *curl)
|
||||
{
|
||||
struct Curl_easy *data = curl;
|
||||
if(!GOOD_EASY_HANDLE(data))
|
||||
return;
|
||||
struct Curl_eapi_guard guard;
|
||||
|
||||
Curl_req_hard_reset(&data->req, data);
|
||||
Curl_hash_clean(&data->meta_hash);
|
||||
if(CURL_EAPI_ENTER(&guard, curl, easy_reset, NULL)) {
|
||||
struct Curl_easy *data = curl;
|
||||
|
||||
/* clear all meta data */
|
||||
Curl_meta_reset(data);
|
||||
/* zero out UserDefined data: */
|
||||
Curl_freeset(data);
|
||||
memset(&data->set, 0, sizeof(struct UserDefined));
|
||||
Curl_init_userdefined(data);
|
||||
Curl_req_hard_reset(&data->req, data);
|
||||
Curl_hash_clean(&data->meta_hash);
|
||||
|
||||
/* zero out Progress data: */
|
||||
memset(&data->progress, 0, sizeof(struct Progress));
|
||||
/* clear all meta data */
|
||||
Curl_meta_reset(data);
|
||||
/* zero out UserDefined data: */
|
||||
Curl_freeset(data);
|
||||
memset(&data->set, 0, sizeof(struct UserDefined));
|
||||
Curl_init_userdefined(data);
|
||||
|
||||
/* zero out PureInfo data: */
|
||||
Curl_initinfo(data);
|
||||
/* zero out Progress data: */
|
||||
memset(&data->progress, 0, sizeof(struct Progress));
|
||||
|
||||
data->progress.hide = TRUE;
|
||||
data->state.current_speed = -1; /* init to negative == impossible */
|
||||
data->state.recent_conn_id = -1; /* clear remembered connection id */
|
||||
/* zero out PureInfo data: */
|
||||
Curl_initinfo(data);
|
||||
|
||||
/* zero out authentication data: */
|
||||
memset(&data->state.authhost, 0, sizeof(struct auth));
|
||||
memset(&data->state.authproxy, 0, sizeof(struct auth));
|
||||
data->progress.hide = TRUE;
|
||||
data->state.current_speed = -1; /* init to negative == impossible */
|
||||
data->state.recent_conn_id = -1; /* clear remembered connection id */
|
||||
|
||||
/* zero out authentication data: */
|
||||
memset(&data->state.authhost, 0, sizeof(struct auth));
|
||||
memset(&data->state.authproxy, 0, sizeof(struct auth));
|
||||
|
||||
#if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_DIGEST_AUTH)
|
||||
Curl_http_auth_cleanup_digest(data);
|
||||
Curl_http_auth_cleanup_digest(data);
|
||||
#endif
|
||||
data->master_mid = UINT32_MAX;
|
||||
data->master_mid = UINT32_MAX;
|
||||
}
|
||||
CURL_EAPI_LEAVE(&guard);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -1137,63 +1162,60 @@ void curl_easy_reset(CURL *curl)
|
|||
*/
|
||||
CURLcode curl_easy_pause(CURL *curl, int action)
|
||||
{
|
||||
struct Curl_eapi_guard guard;
|
||||
CURLcode result = CURLE_OK;
|
||||
bool changed = FALSE;
|
||||
struct Curl_easy *data = curl;
|
||||
bool recv_paused, recv_paused_new;
|
||||
bool send_paused, send_paused_new;
|
||||
uint8_t in_c;
|
||||
|
||||
if(!GOOD_EASY_HANDLE(data) || !data->conn)
|
||||
/* crazy input, do not continue */
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
if(CURL_EAPI_ENTER(&guard, curl, easy_pause, &result)) {
|
||||
bool changed = FALSE;
|
||||
struct Curl_easy *data = curl;
|
||||
bool recv_paused, recv_paused_new;
|
||||
bool send_paused, send_paused_new;
|
||||
|
||||
in_c = Curl_is_in_callback(data);
|
||||
if(in_c == IN_CALLBACK_FORBID_EASY_PAUSE)
|
||||
return CURLE_RECURSIVE_API_CALL;
|
||||
if(!data->conn) {
|
||||
/* crazy input, do not continue */
|
||||
result = CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
recv_paused = Curl_xfer_recv_is_paused(data);
|
||||
recv_paused_new = (action & CURLPAUSE_RECV);
|
||||
send_paused = Curl_xfer_send_is_paused(data);
|
||||
send_paused_new = (action & CURLPAUSE_SEND);
|
||||
recv_paused = Curl_xfer_recv_is_paused(data);
|
||||
recv_paused_new = (action & CURLPAUSE_RECV);
|
||||
send_paused = Curl_xfer_send_is_paused(data);
|
||||
send_paused_new = (action & CURLPAUSE_SEND);
|
||||
|
||||
if((send_paused != send_paused_new) ||
|
||||
(send_paused_new != Curl_creader_is_paused(data))) {
|
||||
changed = TRUE;
|
||||
result = Curl_1st_fatal(
|
||||
result, Curl_xfer_pause_send(data, send_paused_new));
|
||||
}
|
||||
if((send_paused != send_paused_new) ||
|
||||
(send_paused_new != Curl_creader_is_paused(data))) {
|
||||
changed = TRUE;
|
||||
result = Curl_1st_fatal(
|
||||
result, Curl_xfer_pause_send(data, send_paused_new));
|
||||
}
|
||||
|
||||
if(recv_paused != recv_paused_new) {
|
||||
changed = TRUE;
|
||||
result = Curl_1st_fatal(
|
||||
result, Curl_xfer_pause_recv(data, recv_paused_new));
|
||||
}
|
||||
if(recv_paused != recv_paused_new) {
|
||||
changed = TRUE;
|
||||
result = Curl_1st_fatal(
|
||||
result, Curl_xfer_pause_recv(data, recv_paused_new));
|
||||
}
|
||||
|
||||
/* If not completely pausing both directions now, run again in any case. */
|
||||
if(!Curl_xfer_is_blocked(data)) {
|
||||
/* reset the too-slow time keeper */
|
||||
data->state.keeps_speed.tv_sec = 0;
|
||||
if(data->multi) {
|
||||
Curl_multi_mark_dirty(data); /* make it run */
|
||||
/* On changes, tell application to update its timers. */
|
||||
if(changed) {
|
||||
if(Curl_update_timer(data->multi) && !result)
|
||||
result = CURLE_ABORTED_BY_CALLBACK;
|
||||
/* If not completely pausing both directions, run again in any case. */
|
||||
if(!Curl_xfer_is_blocked(data)) {
|
||||
/* reset the too-slow time keeper */
|
||||
data->state.keeps_speed.tv_sec = 0;
|
||||
if(data->multi) {
|
||||
Curl_multi_mark_dirty(data); /* make it run */
|
||||
/* On changes, tell application to update its timers. */
|
||||
if(changed) {
|
||||
if(Curl_update_timer(data->multi) && !result)
|
||||
result = CURLE_ABORTED_BY_CALLBACK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(!result && changed && !data->state.done && data->multi)
|
||||
/* pause/unpausing may result in multi event changes */
|
||||
if(Curl_multi_ev_assess_xfer(data->multi, data) && !result)
|
||||
result = CURLE_ABORTED_BY_CALLBACK;
|
||||
}
|
||||
|
||||
if(!result && changed && !data->state.done && data->multi)
|
||||
/* pause/unpausing may result in multi event changes */
|
||||
if(Curl_multi_ev_assess_xfer(data->multi, data) && !result)
|
||||
result = CURLE_ABORTED_BY_CALLBACK;
|
||||
|
||||
if(in_c)
|
||||
/* this might have called a callback recursively which might have set this
|
||||
to false again on exit */
|
||||
Curl_set_in_callback(data, in_c);
|
||||
|
||||
out:
|
||||
CURL_EAPI_LEAVE(&guard);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -1226,16 +1248,11 @@ static CURLcode easy_connection(struct Curl_easy *data,
|
|||
* curl_easy_perform() with CURLOPT_CONNECT_ONLY option.
|
||||
* Returns CURLE_OK on success, error code on error.
|
||||
*/
|
||||
CURLcode curl_easy_recv(CURL *curl, void *buffer, size_t buflen, size_t *n)
|
||||
CURLcode Curl_easy_recv(struct Curl_easy *data,
|
||||
void *buffer, size_t buflen, size_t *n)
|
||||
{
|
||||
CURLcode result;
|
||||
struct connectdata *c;
|
||||
struct Curl_easy *data = curl;
|
||||
|
||||
if(!GOOD_EASY_HANDLE(data))
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
if(Curl_is_in_callback(data))
|
||||
return CURLE_RECURSIVE_API_CALL;
|
||||
|
||||
result = easy_connection(data, &c);
|
||||
if(result)
|
||||
|
|
@ -1250,6 +1267,18 @@ CURLcode curl_easy_recv(CURL *curl, void *buffer, size_t buflen, size_t *n)
|
|||
return Curl_conn_recv(data, FIRSTSOCKET, buffer, buflen, n);
|
||||
}
|
||||
|
||||
CURLcode curl_easy_recv(CURL *curl, void *buffer, size_t buflen, size_t *n)
|
||||
{
|
||||
struct Curl_eapi_guard guard;
|
||||
CURLcode result;
|
||||
|
||||
if(CURL_EAPI_ENTER(&guard, curl, easy_recv, &result)) {
|
||||
result = Curl_easy_recv(curl, buffer, buflen, n);
|
||||
}
|
||||
CURL_EAPI_LEAVE(&guard);
|
||||
return result;
|
||||
}
|
||||
|
||||
#ifndef CURL_DISABLE_WEBSOCKETS
|
||||
CURLcode Curl_connect_only_attach(struct Curl_easy *data)
|
||||
{
|
||||
|
|
@ -1307,16 +1336,17 @@ CURLcode Curl_senddata(struct Curl_easy *data, const void *buffer,
|
|||
CURLcode curl_easy_send(CURL *curl, const void *buffer, size_t buflen,
|
||||
size_t *n)
|
||||
{
|
||||
size_t written = 0;
|
||||
struct Curl_eapi_guard guard;
|
||||
CURLcode result;
|
||||
struct Curl_easy *data = curl;
|
||||
if(!GOOD_EASY_HANDLE(data))
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
if(Curl_is_in_callback(data))
|
||||
return CURLE_RECURSIVE_API_CALL;
|
||||
|
||||
result = Curl_senddata(data, buffer, buflen, &written);
|
||||
*n = written;
|
||||
if(CURL_EAPI_ENTER(&guard, curl, easy_send, &result)) {
|
||||
struct Curl_easy *data = curl;
|
||||
size_t written = 0;
|
||||
|
||||
result = Curl_senddata(data, buffer, buflen, &written);
|
||||
*n = written;
|
||||
}
|
||||
CURL_EAPI_LEAVE(&guard);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -1325,16 +1355,15 @@ CURLcode curl_easy_send(CURL *curl, const void *buffer, size_t buflen,
|
|||
*/
|
||||
CURLcode curl_easy_upkeep(CURL *curl)
|
||||
{
|
||||
struct Curl_easy *data = curl;
|
||||
/* Verify that we got an easy handle we can work with. */
|
||||
if(!GOOD_EASY_HANDLE(data))
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
struct Curl_eapi_guard guard;
|
||||
CURLcode result;
|
||||
|
||||
if(Curl_is_in_callback(data))
|
||||
return CURLE_RECURSIVE_API_CALL;
|
||||
|
||||
/* Use the common function to keep connections alive. */
|
||||
return Curl_cpool_upkeep(data);
|
||||
if(CURL_EAPI_ENTER(&guard, curl, easy_upkeep, &result)) {
|
||||
/* Use the common function to keep connections alive. */
|
||||
result = Curl_cpool_upkeep((struct Curl_easy *)curl);
|
||||
}
|
||||
CURL_EAPI_LEAVE(&guard);
|
||||
return result;
|
||||
}
|
||||
|
||||
CURLcode curl_easy_ssls_import(CURL *curl, const char *session_key,
|
||||
|
|
@ -1342,13 +1371,15 @@ CURLcode curl_easy_ssls_import(CURL *curl, const char *session_key,
|
|||
const unsigned char *sdata, size_t sdata_len)
|
||||
{
|
||||
#if defined(USE_SSL) && defined(USE_SSLS_EXPORT)
|
||||
struct Curl_easy *data = curl;
|
||||
if(!GOOD_EASY_HANDLE(data))
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
if(Curl_is_in_callback(data) || Curl_ssl_scache_is_locked(data))
|
||||
return CURLE_RECURSIVE_API_CALL;
|
||||
return Curl_ssl_session_import(data, session_key,
|
||||
shmac, shmac_len, sdata, sdata_len);
|
||||
struct Curl_eapi_guard guard;
|
||||
CURLcode result;
|
||||
|
||||
if(CURL_EAPI_ENTER(&guard, curl, easy_ssls_import, &result)) {
|
||||
result = Curl_ssl_session_import((struct Curl_easy *)curl, session_key,
|
||||
shmac, shmac_len, sdata, sdata_len);
|
||||
}
|
||||
CURL_EAPI_LEAVE(&guard);
|
||||
return result;
|
||||
#else
|
||||
(void)curl;
|
||||
(void)session_key;
|
||||
|
|
@ -1365,12 +1396,15 @@ CURLcode curl_easy_ssls_export(CURL *curl,
|
|||
void *userptr)
|
||||
{
|
||||
#if defined(USE_SSL) && defined(USE_SSLS_EXPORT)
|
||||
struct Curl_easy *data = curl;
|
||||
if(!GOOD_EASY_HANDLE(data))
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
if(Curl_is_in_callback(data) || Curl_ssl_scache_is_locked(data))
|
||||
return CURLE_RECURSIVE_API_CALL;
|
||||
return Curl_ssl_session_export(data, export_fn, userptr);
|
||||
struct Curl_eapi_guard guard;
|
||||
CURLcode result;
|
||||
|
||||
if(CURL_EAPI_ENTER(&guard, curl, easy_ssls_export, &result)) {
|
||||
result = Curl_ssl_session_export((struct Curl_easy *)curl,
|
||||
export_fn, userptr);
|
||||
}
|
||||
CURL_EAPI_LEAVE(&guard);
|
||||
return result;
|
||||
#else
|
||||
(void)curl;
|
||||
(void)export_fn;
|
||||
|
|
|
|||
20
lib/ftp.c
20
lib/ftp.c
|
|
@ -1714,10 +1714,11 @@ static CURLcode ftp_state_ul_setup(struct Curl_easy *data,
|
|||
|
||||
/* Let's read off the proper amount of bytes from the input. */
|
||||
if(data->set.seek_func) {
|
||||
Curl_set_in_callback(data, TRUE);
|
||||
struct Curl_mapi_guard guard;
|
||||
CURL_CBAPI_START(&guard, data, easy_seek_func);
|
||||
seekerr = data->set.seek_func(data->set.seek_client,
|
||||
data->state.resume_from, SEEK_SET);
|
||||
Curl_set_in_callback(data, FALSE);
|
||||
CURL_CBAPI_END(&guard);
|
||||
}
|
||||
|
||||
if(seekerr != CURL_SEEKFUNC_OK) {
|
||||
|
|
@ -3658,9 +3659,10 @@ static void ftp_done_wildcard(struct Curl_easy *data, struct ftp_conn *ftpc)
|
|||
{
|
||||
if(data->state.wildcardmatch) {
|
||||
if(data->set.chunk_end && ftpc->file) {
|
||||
Curl_set_in_callback(data, TRUE);
|
||||
struct Curl_mapi_guard guard;
|
||||
CURL_CBAPI_START(&guard, data, easy_chunk_end);
|
||||
data->set.chunk_end(data->set.wildcardptr);
|
||||
Curl_set_in_callback(data, FALSE);
|
||||
CURL_CBAPI_END(&guard);
|
||||
freedirs(ftpc);
|
||||
}
|
||||
ftpc->known_filesize = -1;
|
||||
|
|
@ -4101,11 +4103,12 @@ static CURLcode wc_statemach(struct Curl_easy *data,
|
|||
infof(data, "Wildcard - START of \"%s\"", finfo->filename);
|
||||
if(data->set.chunk_bgn) {
|
||||
long userresponse;
|
||||
Curl_set_in_callback(data, TRUE);
|
||||
struct Curl_mapi_guard guard;
|
||||
CURL_CBAPI_START(&guard, data, easy_chunk_bgn);
|
||||
userresponse = data->set.chunk_bgn(
|
||||
finfo, data->set.wildcardptr,
|
||||
(int)Curl_llist_count(&wildcard->filelist));
|
||||
Curl_set_in_callback(data, FALSE);
|
||||
CURL_CBAPI_END(&guard);
|
||||
switch(userresponse) {
|
||||
case CURL_CHUNK_BGN_FUNC_SKIP:
|
||||
infof(data, "Wildcard - \"%s\" skipped by user", finfo->filename);
|
||||
|
|
@ -4143,9 +4146,10 @@ static CURLcode wc_statemach(struct Curl_easy *data,
|
|||
|
||||
case CURLWC_SKIP: {
|
||||
if(data->set.chunk_end) {
|
||||
Curl_set_in_callback(data, TRUE);
|
||||
struct Curl_mapi_guard guard;
|
||||
CURL_CBAPI_START(&guard, data, easy_chunk_end);
|
||||
data->set.chunk_end(data->set.wildcardptr);
|
||||
Curl_set_in_callback(data, FALSE);
|
||||
CURL_CBAPI_END(&guard);
|
||||
}
|
||||
Curl_node_remove(Curl_llist_head(&wildcard->filelist));
|
||||
wildcard->state = (Curl_llist_count(&wildcard->filelist) == 0) ?
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
13
lib/http2.c
13
lib/http2.c
|
|
@ -817,11 +817,14 @@ static int push_promise(struct Curl_cfilter *cf,
|
|||
goto fail;
|
||||
}
|
||||
|
||||
Curl_set_in_callback(data, TRUE);
|
||||
rv = data->multi->push_cb(data, newhandle,
|
||||
stream->push_headers_used, &heads,
|
||||
data->multi->push_userp);
|
||||
Curl_set_in_callback(data, FALSE);
|
||||
{
|
||||
struct Curl_mapi_guard guard;
|
||||
CURL_CBAPI_START(&guard, data, multi_push_cb);
|
||||
rv = data->multi->push_cb(data, newhandle,
|
||||
stream->push_headers_used, &heads,
|
||||
data->multi->push_userp);
|
||||
CURL_CBAPI_END(&guard);
|
||||
}
|
||||
|
||||
/* free the headers again */
|
||||
free_push_headers(stream);
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
825
lib/multi.c
825
lib/multi.c
File diff suppressed because it is too large
Load diff
|
|
@ -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");
|
||||
|
|
|
|||
25
lib/sendf.c
25
lib/sendf.c
|
|
@ -678,9 +678,10 @@ static CURLcode cr_in_read(struct Curl_easy *data,
|
|||
}
|
||||
nread = 0;
|
||||
if(ctx->read_cb && blen) {
|
||||
Curl_set_in_callback(data, TRUE);
|
||||
struct Curl_mapi_guard guard;
|
||||
CURL_CBAPI_START(&guard, data, easy_cr_in_read);
|
||||
nread = ctx->read_cb(buf, 1, blen, ctx->cb_user_data);
|
||||
Curl_set_in_callback(data, FALSE);
|
||||
CURL_CBAPI_END(&guard);
|
||||
ctx->has_used_cb = TRUE;
|
||||
}
|
||||
|
||||
|
|
@ -778,9 +779,10 @@ static CURLcode cr_in_resume_from(struct Curl_easy *data,
|
|||
return CURLE_READ_ERROR;
|
||||
|
||||
if(data->set.seek_func) {
|
||||
Curl_set_in_callback(data, TRUE);
|
||||
struct Curl_mapi_guard guard;
|
||||
CURL_CBAPI_START(&guard, data, easy_seek_func);
|
||||
seekerr = data->set.seek_func(data->set.seek_client, offset, SEEK_SET);
|
||||
Curl_set_in_callback(data, FALSE);
|
||||
CURL_CBAPI_END(&guard);
|
||||
}
|
||||
|
||||
if(seekerr != CURL_SEEKFUNC_OK) {
|
||||
|
|
@ -792,6 +794,7 @@ static CURLcode cr_in_resume_from(struct Curl_easy *data,
|
|||
}
|
||||
/* when seekerr == CURL_SEEKFUNC_CANTSEEK (cannot seek to offset) */
|
||||
do {
|
||||
struct Curl_mapi_guard guard;
|
||||
char scratch[4 * 1024];
|
||||
size_t readthisamountnow =
|
||||
(offset - passed > (curl_off_t)sizeof(scratch)) ?
|
||||
|
|
@ -799,10 +802,10 @@ static CURLcode cr_in_resume_from(struct Curl_easy *data,
|
|||
curlx_sotouz(offset - passed);
|
||||
size_t actuallyread;
|
||||
|
||||
Curl_set_in_callback(data, TRUE);
|
||||
CURL_CBAPI_START(&guard, data, easy_cr_in_resume_from);
|
||||
actuallyread = ctx->read_cb(scratch, 1, readthisamountnow,
|
||||
ctx->cb_user_data);
|
||||
Curl_set_in_callback(data, FALSE);
|
||||
CURL_CBAPI_END(&guard);
|
||||
|
||||
passed += actuallyread;
|
||||
if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
|
||||
|
|
@ -838,11 +841,12 @@ static CURLcode cr_in_rewind(struct Curl_easy *data,
|
|||
return CURLE_OK;
|
||||
|
||||
if(data->set.seek_func) {
|
||||
struct Curl_mapi_guard guard;
|
||||
int err;
|
||||
|
||||
Curl_set_in_callback(data, TRUE);
|
||||
CURL_CBAPI_START(&guard, data, easy_seek_func);
|
||||
err = (data->set.seek_func)(data->set.seek_client, 0, SEEK_SET);
|
||||
Curl_set_in_callback(data, FALSE);
|
||||
CURL_CBAPI_END(&guard);
|
||||
CURL_TRC_READ(data, "cr_in, rewind via set.seek_func -> %d", err);
|
||||
if(err) {
|
||||
failf(data, "seek callback returned error %d", err);
|
||||
|
|
@ -850,12 +854,13 @@ static CURLcode cr_in_rewind(struct Curl_easy *data,
|
|||
}
|
||||
}
|
||||
else if(data->set.ioctl_func) {
|
||||
struct Curl_mapi_guard guard;
|
||||
curlioerr err;
|
||||
|
||||
Curl_set_in_callback(data, TRUE);
|
||||
CURL_CBAPI_START(&guard, data, easy_ioctl_func);
|
||||
err = (data->set.ioctl_func)(data, CURLIOCMD_RESTARTREAD,
|
||||
data->set.ioctl_client);
|
||||
Curl_set_in_callback(data, FALSE);
|
||||
CURL_CBAPI_END(&guard);
|
||||
CURL_TRC_READ(data, "cr_in, rewind via set.ioctl_func -> %d", (int)err);
|
||||
if(err) {
|
||||
failf(data, "ioctl callback returned error %d", (int)err);
|
||||
|
|
|
|||
21
lib/setopt.c
21
lib/setopt.c
|
|
@ -2916,23 +2916,24 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option, va_list param)
|
|||
* NOTE: This is one of few API functions that are allowed to be called from
|
||||
* within a callback.
|
||||
*/
|
||||
|
||||
#undef curl_easy_setopt
|
||||
CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...)
|
||||
{
|
||||
va_list arg;
|
||||
struct Curl_eapi_guard guard;
|
||||
CURLcode result;
|
||||
struct Curl_easy *data = curl;
|
||||
|
||||
if(!data)
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
if(CURL_EAPI_ENTER(&guard, curl, easy_setopt, &result)) {
|
||||
struct Curl_easy *data = curl;
|
||||
va_list arg;
|
||||
|
||||
va_start(arg, option);
|
||||
va_start(arg, option);
|
||||
|
||||
result = Curl_vsetopt(data, option, arg);
|
||||
result = Curl_vsetopt(data, option, arg);
|
||||
|
||||
va_end(arg);
|
||||
if(result == CURLE_BAD_FUNCTION_ARGUMENT)
|
||||
failf(data, "setopt 0x%x got bad argument", (unsigned int)option);
|
||||
va_end(arg);
|
||||
if(result == CURLE_BAD_FUNCTION_ARGUMENT)
|
||||
failf(data, "setopt 0x%x got bad argument", (unsigned int)option);
|
||||
}
|
||||
CURL_EAPI_LEAVE(&guard);
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,6 +1212,26 @@ struct Curl_easy {
|
|||
/* First a simple identifier to easier detect if a user mix up this easy
|
||||
handle with a multi handle. Set this to CURLEASY_MAGIC_NUMBER */
|
||||
uint32_t magic;
|
||||
/* once an easy handle is added to a multi, either explicitly by the
|
||||
* libcurl application or implicitly during `curl_easy_perform()`,
|
||||
* a unique identifier inside this one multi instance. */
|
||||
uint32_t mid;
|
||||
CURLMstate mstate; /* the handle's state */
|
||||
CURLcode result; /* previous result */
|
||||
|
||||
struct connectdata *conn;
|
||||
struct Curl_multi *multi; /* if non-NULL, points to the multi handle
|
||||
struct to which this "belongs" when used by
|
||||
the multi interface */
|
||||
struct Curl_eapi_stack callstack; /* easy api calls ongoing */
|
||||
|
||||
struct Curl_share *share; /* Share, handles global variable mutexing */
|
||||
|
||||
struct Curl_multi *multi_easy; /* if non-NULL, points to the multi handle
|
||||
struct to which this "belongs" when used
|
||||
by the easy interface */
|
||||
struct Curl_message msg; /* A single posted message. */
|
||||
|
||||
/* once an easy handle is tied to a connection pool a non-negative number to
|
||||
distinguish this transfer from other using the same pool. For easier
|
||||
tracking in log output. This may wrap around after LONG_MAX to 0 again,
|
||||
|
|
@ -1231,28 +1239,9 @@ struct Curl_easy {
|
|||
uniqueness either IFF more than one connection pool is used by the
|
||||
libcurl application. */
|
||||
curl_off_t id;
|
||||
/* once an easy handle is added to a multi, either explicitly by the
|
||||
* libcurl application or implicitly during `curl_easy_perform()`,
|
||||
* a unique identifier inside this one multi instance. */
|
||||
uint32_t mid;
|
||||
uint32_t master_mid; /* if set, this transfer belongs to a master */
|
||||
multi_sub_xfer_done_cb *sub_xfer_done;
|
||||
|
||||
struct connectdata *conn;
|
||||
|
||||
CURLMstate mstate; /* the handle's state */
|
||||
CURLcode result; /* previous result */
|
||||
|
||||
struct Curl_message msg; /* A single posted message. */
|
||||
|
||||
struct Curl_multi *multi; /* if non-NULL, points to the multi handle
|
||||
struct to which this "belongs" when used by
|
||||
the multi interface */
|
||||
struct Curl_multi *multi_easy; /* if non-NULL, points to the multi handle
|
||||
struct to which this "belongs" when used
|
||||
by the easy interface */
|
||||
struct Curl_share *share; /* Share, handles global variable mutexing */
|
||||
|
||||
/* `meta_hash` is a general key-value store for implementations
|
||||
* with the lifetime of the easy handle.
|
||||
* Elements need to be added with their own destructor to be invoked when
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
371
lib/ws.c
371
lib/ws.c
|
|
@ -762,7 +762,7 @@ static CURLcode ws_cw_write(struct Curl_easy *data,
|
|||
|
||||
out:
|
||||
if(!result) {
|
||||
result = ws_flush(data, ws, Curl_is_in_callback(data));
|
||||
result = ws_flush(data, ws, Curl_api_is_in_callback(data));
|
||||
if(result == CURLE_AGAIN)
|
||||
result = CURLE_OK;
|
||||
}
|
||||
|
|
@ -1110,7 +1110,7 @@ static CURLcode ws_enc_send(struct Curl_easy *data,
|
|||
}
|
||||
}
|
||||
else {
|
||||
result = ws_flush(data, ws, Curl_is_in_callback(data));
|
||||
result = ws_flush(data, ws, Curl_api_is_in_callback(data));
|
||||
if(result)
|
||||
return result;
|
||||
|
||||
|
|
@ -1143,7 +1143,7 @@ static CURLcode ws_enc_send(struct Curl_easy *data,
|
|||
}
|
||||
|
||||
/* flush, blocking when in callback */
|
||||
result = ws_flush(data, ws, Curl_is_in_callback(data));
|
||||
result = ws_flush(data, ws, Curl_api_is_in_callback(data));
|
||||
if(!result && ws->sendbuf_payload > 0) {
|
||||
*pnsent += ws->sendbuf_payload;
|
||||
buffer += ws->sendbuf_payload;
|
||||
|
|
@ -1581,104 +1581,116 @@ static CURLcode nw_in_recv(void *reader_ctx,
|
|||
size_t *pnread)
|
||||
{
|
||||
struct Curl_easy *data = reader_ctx;
|
||||
return curl_easy_recv(data, buf, buflen, pnread);
|
||||
return Curl_easy_recv(data, buf, buflen, pnread);
|
||||
}
|
||||
|
||||
CURLcode curl_ws_recv(CURL *curl, void *buffer,
|
||||
size_t buflen, size_t *recv,
|
||||
const struct curl_ws_frame **metap)
|
||||
{
|
||||
struct Curl_easy *data = curl;
|
||||
struct connectdata *conn;
|
||||
struct websocket *ws;
|
||||
struct ws_collect ctx;
|
||||
struct Curl_eapi_guard guard;
|
||||
CURLcode result = CURLE_OK;
|
||||
|
||||
*recv = 0;
|
||||
*metap = NULL;
|
||||
if(!GOOD_EASY_HANDLE(data) || (buflen && !buffer))
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
if(CURL_EAPI_ENTER(&guard, curl, ws_recv, &result)) {
|
||||
struct Curl_easy *data = curl;
|
||||
struct connectdata *conn;
|
||||
struct websocket *ws;
|
||||
struct ws_collect ctx;
|
||||
|
||||
conn = data->conn;
|
||||
if(!conn) {
|
||||
/* Unhappy hack with lifetimes of transfers and connection */
|
||||
if(!data->set.connect_only) {
|
||||
failf(data, "[WS] CONNECT_ONLY is required");
|
||||
return CURLE_UNSUPPORTED_PROTOCOL;
|
||||
if(buflen && !buffer) {
|
||||
result = CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
Curl_getconnectinfo(data, &conn);
|
||||
conn = data->conn;
|
||||
if(!conn) {
|
||||
failf(data, "[WS] connection not found");
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
}
|
||||
}
|
||||
ws = Curl_conn_meta_get(conn, CURL_META_PROTO_WS_CONN);
|
||||
if(!ws) {
|
||||
failf(data, "[WS] connection is not setup for websocket");
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
}
|
||||
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
ctx.data = data;
|
||||
ctx.ws = ws;
|
||||
ctx.buffer = buffer;
|
||||
ctx.buflen = buflen;
|
||||
|
||||
while(1) {
|
||||
CURLcode result;
|
||||
|
||||
/* receive more when our buffer is empty */
|
||||
if(Curl_bufq_is_empty(&ws->recvbuf)) {
|
||||
size_t n;
|
||||
result = Curl_bufq_slurp(&ws->recvbuf, nw_in_recv, data, &n);
|
||||
if(result)
|
||||
return result;
|
||||
else if(n == 0) {
|
||||
/* connection closed */
|
||||
infof(data, "[WS] connection expectedly closed?");
|
||||
return CURLE_GOT_NOTHING;
|
||||
/* Unhappy hack with lifetimes of transfers and connection */
|
||||
if(!data->set.connect_only) {
|
||||
failf(data, "[WS] CONNECT_ONLY is required");
|
||||
result = CURLE_UNSUPPORTED_PROTOCOL;
|
||||
goto out;
|
||||
}
|
||||
CURL_TRC_WS(data, "curl_ws_recv, added %zu bytes from network",
|
||||
Curl_bufq_len(&ws->recvbuf));
|
||||
}
|
||||
|
||||
result = ws_dec_pass(&ws->dec, data, &ws->recvbuf,
|
||||
ws_client_collect, &ctx);
|
||||
if(result == CURLE_AGAIN) {
|
||||
if(!ctx.written) {
|
||||
ws_dec_info(&ws->dec, data, "need more input");
|
||||
continue; /* nothing written, try more input */
|
||||
Curl_getconnectinfo(data, &conn);
|
||||
if(!conn) {
|
||||
failf(data, "[WS] connection not found");
|
||||
result = CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if(result) {
|
||||
return result;
|
||||
ws = Curl_conn_meta_get(conn, CURL_META_PROTO_WS_CONN);
|
||||
if(!ws) {
|
||||
failf(data, "[WS] connection is not setup for websocket");
|
||||
result = CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
goto out;
|
||||
}
|
||||
else if(ctx.written) {
|
||||
/* The decoded frame is passed back to our caller.
|
||||
* There are frames like PING were we auto-respond to and
|
||||
* that we do not return. For these `ctx.written` is not set. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* update frame information to be passed back */
|
||||
update_meta(ws, ctx.frame_age, ctx.frame_flags, ctx.payload_offset,
|
||||
ctx.payload_len, ctx.bufidx);
|
||||
*metap = &ws->recvframe;
|
||||
*recv = ws->recvframe.len;
|
||||
CURL_TRC_WS(data, "curl_ws_recv(len=%zu) -> %zu bytes (frame at %"
|
||||
FMT_OFF_T ", %" FMT_OFF_T " left)",
|
||||
buflen, *recv, ws->recvframe.offset,
|
||||
ws->recvframe.bytesleft);
|
||||
/* all's well, try to send any pending control. we do not know
|
||||
* when the application will call `curl_ws_send()` again. */
|
||||
if(!data->set.ws_raw_mode && ws->pending.type) {
|
||||
CURLcode r2 = ws_enc_add_pending(data, ws);
|
||||
if(!r2)
|
||||
(void)ws_flush(data, ws, Curl_is_in_callback(data));
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
ctx.data = data;
|
||||
ctx.ws = ws;
|
||||
ctx.buffer = buffer;
|
||||
ctx.buflen = buflen;
|
||||
|
||||
while(1) {
|
||||
/* receive more when our buffer is empty */
|
||||
if(Curl_bufq_is_empty(&ws->recvbuf)) {
|
||||
size_t n;
|
||||
result = Curl_bufq_slurp(&ws->recvbuf, nw_in_recv, data, &n);
|
||||
if(result)
|
||||
goto out;
|
||||
else if(n == 0) {
|
||||
/* connection closed */
|
||||
infof(data, "[WS] connection expectedly closed?");
|
||||
result = CURLE_GOT_NOTHING;
|
||||
goto out;
|
||||
}
|
||||
CURL_TRC_WS(data, "curl_ws_recv, added %zu bytes from network",
|
||||
Curl_bufq_len(&ws->recvbuf));
|
||||
}
|
||||
|
||||
result = ws_dec_pass(&ws->dec, data, &ws->recvbuf,
|
||||
ws_client_collect, &ctx);
|
||||
if(result == CURLE_AGAIN) {
|
||||
if(!ctx.written) {
|
||||
ws_dec_info(&ws->dec, data, "need more input");
|
||||
continue; /* nothing written, try more input */
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if(result) {
|
||||
goto out;
|
||||
}
|
||||
else if(ctx.written) {
|
||||
/* The decoded frame is passed back to our caller.
|
||||
* There are frames like PING were we auto-respond to and
|
||||
* that we do not return. For these `ctx.written` is not set. */
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* update frame information to be passed back */
|
||||
update_meta(ws, ctx.frame_age, ctx.frame_flags, ctx.payload_offset,
|
||||
ctx.payload_len, ctx.bufidx);
|
||||
*metap = &ws->recvframe;
|
||||
*recv = ws->recvframe.len;
|
||||
CURL_TRC_WS(data, "curl_ws_recv(len=%zu) -> %zu bytes (frame at %"
|
||||
FMT_OFF_T ", %" FMT_OFF_T " left)",
|
||||
buflen, *recv, ws->recvframe.offset,
|
||||
ws->recvframe.bytesleft);
|
||||
/* all's well, try to send any pending control. we do not know
|
||||
* when the application will call `curl_ws_send()` again. */
|
||||
if(!data->set.ws_raw_mode && ws->pending.type) {
|
||||
CURLcode r2 = ws_enc_add_pending(data, ws);
|
||||
if(!r2)
|
||||
(void)ws_flush(data, ws, Curl_api_is_in_callback(data));
|
||||
}
|
||||
result = CURLE_OK;
|
||||
}
|
||||
return CURLE_OK;
|
||||
out:
|
||||
CURL_EAPI_LEAVE(&guard);
|
||||
return result;
|
||||
}
|
||||
|
||||
static CURLcode ws_flush(struct Curl_easy *data, struct websocket *ws,
|
||||
|
|
@ -1721,7 +1733,7 @@ static CURLcode ws_flush(struct Curl_easy *data, struct websocket *ws,
|
|||
result = ws_send_raw_blocking(data, ws, (const char *)out, outlen);
|
||||
n = result ? 0 : outlen;
|
||||
}
|
||||
else if(data->set.connect_only || Curl_is_in_callback(data))
|
||||
else if(data->set.connect_only || Curl_api_is_in_callback(data))
|
||||
result = Curl_senddata(data, out, outlen, &n);
|
||||
else {
|
||||
result = Curl_xfer_send(data, out, outlen, FALSE, &n);
|
||||
|
|
@ -1805,7 +1817,7 @@ static CURLcode ws_send_raw(struct Curl_easy *data, const void *buffer,
|
|||
if(!buflen)
|
||||
return CURLE_OK;
|
||||
|
||||
if(Curl_is_in_callback(data)) {
|
||||
if(Curl_api_is_in_callback(data)) {
|
||||
/* When invoked from inside callbacks, we do a blocking send as the
|
||||
* callback will probably not implement partial writes that may then
|
||||
* mess up the ws framing subsequently.
|
||||
|
|
@ -1835,75 +1847,80 @@ CURLcode curl_ws_send(CURL *curl, const void *buffer_arg,
|
|||
curl_off_t fragsize,
|
||||
unsigned int flags)
|
||||
{
|
||||
struct websocket *ws;
|
||||
const uint8_t *buffer = buffer_arg;
|
||||
struct Curl_eapi_guard guard;
|
||||
CURLcode result = CURLE_OK;
|
||||
struct Curl_easy *data = curl;
|
||||
size_t ndummy;
|
||||
size_t *pnsent = sent ? sent : &ndummy;
|
||||
|
||||
if(!GOOD_EASY_HANDLE(data))
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
CURL_TRC_WS(data, "curl_ws_send(len=%zu, fragsize=%" FMT_OFF_T
|
||||
", flags=%x), raw=%d",
|
||||
buflen, fragsize, flags, data->set.ws_raw_mode);
|
||||
if(CURL_EAPI_ENTER(&guard, curl, ws_send, &result)) {
|
||||
struct websocket *ws;
|
||||
const uint8_t *buffer = buffer_arg;
|
||||
struct Curl_easy *data = curl;
|
||||
size_t ndummy;
|
||||
size_t *pnsent = sent ? sent : &ndummy;
|
||||
|
||||
*pnsent = 0;
|
||||
CURL_TRC_WS(data, "curl_ws_send(len=%zu, fragsize=%" FMT_OFF_T
|
||||
", flags=%x), raw=%d",
|
||||
buflen, fragsize, flags, data->set.ws_raw_mode);
|
||||
|
||||
if(!buffer && buflen) {
|
||||
failf(data, "[WS] buffer is NULL when buflen is not");
|
||||
result = CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
goto out;
|
||||
}
|
||||
*pnsent = 0;
|
||||
|
||||
if(!data->conn && data->set.connect_only) {
|
||||
result = Curl_connect_only_attach(data);
|
||||
if(result)
|
||||
if(!buffer && buflen) {
|
||||
failf(data, "[WS] buffer is NULL when buflen is not");
|
||||
result = CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
goto out;
|
||||
}
|
||||
if(!data->conn) {
|
||||
failf(data, "[WS] No associated connection");
|
||||
result = CURLE_SEND_ERROR;
|
||||
goto out;
|
||||
}
|
||||
ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN);
|
||||
if(!ws) {
|
||||
failf(data, "[WS] Not a websocket transfer");
|
||||
result = CURLE_SEND_ERROR;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if(data->set.ws_raw_mode) {
|
||||
/* In raw mode, we write directly to the connection */
|
||||
/* try flushing any content still waiting to be sent. */
|
||||
result = ws_flush(data, ws, FALSE);
|
||||
if(result)
|
||||
if(!data->conn && data->set.connect_only) {
|
||||
result = Curl_connect_only_attach(data);
|
||||
if(result)
|
||||
goto out;
|
||||
}
|
||||
if(!data->conn) {
|
||||
failf(data, "[WS] No associated connection");
|
||||
result = CURLE_SEND_ERROR;
|
||||
goto out;
|
||||
}
|
||||
ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN);
|
||||
if(!ws) {
|
||||
failf(data, "[WS] Not a websocket transfer");
|
||||
result = CURLE_SEND_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(!buffer) {
|
||||
failf(data, "[WS] buffer is NULL in raw mode");
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
if(data->set.ws_raw_mode) {
|
||||
/* In raw mode, we write directly to the connection */
|
||||
/* try flushing any content still waiting to be sent. */
|
||||
result = ws_flush(data, ws, FALSE);
|
||||
if(result)
|
||||
goto out;
|
||||
|
||||
if(!buffer) {
|
||||
failf(data, "[WS] buffer is NULL in raw mode");
|
||||
result = CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
goto out;
|
||||
}
|
||||
if(!sent) {
|
||||
failf(data, "[WS] sent is NULL in raw mode");
|
||||
result = CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
goto out;
|
||||
}
|
||||
if(fragsize || flags) {
|
||||
failf(data, "[WS] fragsize and flags must be zero in raw mode");
|
||||
result = CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
goto out;
|
||||
}
|
||||
result = ws_send_raw(data, buffer, buflen, pnsent);
|
||||
goto out;
|
||||
}
|
||||
if(!sent) {
|
||||
failf(data, "[WS] sent is NULL in raw mode");
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
}
|
||||
if(fragsize || flags) {
|
||||
failf(data, "[WS] fragsize and flags must be zero in raw mode");
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
}
|
||||
result = ws_send_raw(data, buffer, buflen, pnsent);
|
||||
goto out;
|
||||
|
||||
/* Not RAW mode, we do the frame encoding */
|
||||
result = ws_enc_send(data, ws, buffer, buflen, fragsize, flags, pnsent);
|
||||
CURL_TRC_WS(data, "curl_ws_send(len=%zu, fragsize=%" FMT_OFF_T
|
||||
", flags=%x, raw=%d) -> %d, %zu",
|
||||
buflen, fragsize, flags, data->set.ws_raw_mode, (int)result,
|
||||
*pnsent);
|
||||
}
|
||||
|
||||
/* Not RAW mode, we do the frame encoding */
|
||||
result = ws_enc_send(data, ws, buffer, buflen, fragsize, flags, pnsent);
|
||||
|
||||
out:
|
||||
CURL_TRC_WS(data, "curl_ws_send(len=%zu, fragsize=%" FMT_OFF_T
|
||||
", flags=%x, raw=%d) -> %d, %zu",
|
||||
buflen, fragsize, flags, data->set.ws_raw_mode, (int)result,
|
||||
*pnsent);
|
||||
CURL_EAPI_LEAVE(&guard);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
@ -1923,7 +1940,7 @@ const struct curl_ws_frame *curl_ws_meta(CURL *curl)
|
|||
/* we only return something for websocket, called from within the callback
|
||||
when not using raw mode */
|
||||
struct Curl_easy *data = curl;
|
||||
if(GOOD_EASY_HANDLE(data) && Curl_is_in_callback(data) &&
|
||||
if(GOOD_EASY_HANDLE(data) && Curl_api_is_in_callback(data) &&
|
||||
data->conn && !data->set.ws_raw_mode) {
|
||||
struct websocket *ws;
|
||||
ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN);
|
||||
|
|
@ -1937,46 +1954,48 @@ CURL_EXTERN CURLcode curl_ws_start_frame(CURL *curl,
|
|||
unsigned int flags,
|
||||
curl_off_t frame_len)
|
||||
{
|
||||
struct websocket *ws;
|
||||
struct Curl_eapi_guard guard;
|
||||
CURLcode result = CURLE_OK;
|
||||
struct Curl_easy *data = curl;
|
||||
|
||||
if(!GOOD_EASY_HANDLE(data))
|
||||
return CURLE_BAD_FUNCTION_ARGUMENT;
|
||||
if(CURL_EAPI_ENTER(&guard, curl, ws_start_frame, &result)) {
|
||||
struct Curl_easy *data = curl;
|
||||
struct websocket *ws;
|
||||
|
||||
if(data->set.ws_raw_mode) {
|
||||
failf(data, "cannot curl_ws_start_frame() with CURLWS_RAW_MODE enabled");
|
||||
return CURLE_FAILED_INIT;
|
||||
if(data->set.ws_raw_mode) {
|
||||
failf(data, "cannot curl_ws_start_frame() with CURLWS_RAW_MODE enabled");
|
||||
result = CURLE_FAILED_INIT;
|
||||
goto out;
|
||||
}
|
||||
|
||||
CURL_TRC_WS(data, "curl_ws_start_frame(flags=%x, frame_len=%" FMT_OFF_T,
|
||||
flags, frame_len);
|
||||
|
||||
if(!data->conn) {
|
||||
failf(data, "[WS] No associated connection");
|
||||
result = CURLE_SEND_ERROR;
|
||||
goto out;
|
||||
}
|
||||
ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN);
|
||||
if(!ws) {
|
||||
failf(data, "[WS] Not a websocket transfer");
|
||||
result = CURLE_SEND_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(ws->enc.payload_remain) {
|
||||
failf(data, "[WS] previous frame not finished");
|
||||
result = CURLE_SEND_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
result = ws_enc_write_head(data, ws, &ws->enc, flags, frame_len,
|
||||
&ws->sendbuf);
|
||||
if(result)
|
||||
CURL_TRC_WS(data, "curl_start_frame(), error adding frame head %d",
|
||||
(int)result);
|
||||
}
|
||||
|
||||
CURL_TRC_WS(data, "curl_ws_start_frame(flags=%x, frame_len=%" FMT_OFF_T,
|
||||
flags, frame_len);
|
||||
|
||||
if(!data->conn) {
|
||||
failf(data, "[WS] No associated connection");
|
||||
result = CURLE_SEND_ERROR;
|
||||
goto out;
|
||||
}
|
||||
ws = Curl_conn_meta_get(data->conn, CURL_META_PROTO_WS_CONN);
|
||||
if(!ws) {
|
||||
failf(data, "[WS] Not a websocket transfer");
|
||||
result = CURLE_SEND_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if(ws->enc.payload_remain) {
|
||||
failf(data, "[WS] previous frame not finished");
|
||||
result = CURLE_SEND_ERROR;
|
||||
goto out;
|
||||
}
|
||||
|
||||
result = ws_enc_write_head(data, ws, &ws->enc, flags, frame_len,
|
||||
&ws->sendbuf);
|
||||
if(result)
|
||||
CURL_TRC_WS(data, "curl_start_frame(), error adding frame head %d",
|
||||
(int)result);
|
||||
|
||||
out:
|
||||
CURL_EAPI_LEAVE(&guard);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue