TODO fixed: Detect when called from within callbacks

Closes #2302
This commit is contained in:
Björn Stenberg 2018-02-10 15:13:15 +01:00 committed by Daniel Stenberg
parent 43a50a2580
commit b46cfbc068
No known key found for this signature in database
GPG Key ID: 5CC908FDB71E12C2
28 changed files with 312 additions and 29 deletions

View File

@ -22,7 +22,6 @@
1.4 signal-based resolver timeouts 1.4 signal-based resolver timeouts
1.5 get rid of PATH_MAX 1.5 get rid of PATH_MAX
1.6 Modified buffer size approach 1.6 Modified buffer size approach
1.7 Detect when called from within callbacks
1.8 CURLOPT_RESOLVE for any port number 1.8 CURLOPT_RESOLVE for any port number
1.9 Cache negative name resolves 1.9 Cache negative name resolves
1.10 auto-detect proxy 1.10 auto-detect proxy
@ -239,12 +238,6 @@
Dynamically allocate buffer size depending on protocol in use in combination Dynamically allocate buffer size depending on protocol in use in combination
with freeing it after each individual transfer? Other suggestions? with freeing it after each individual transfer? Other suggestions?
1.7 Detect when called from within callbacks
We should set a state variable before calling callbacks, so that we
subsequently can add code within libcurl that returns error if called within
callbacks for when that's not supported.
1.8 CURLOPT_RESOLVE for any port number 1.8 CURLOPT_RESOLVE for any port number
This option allows applications to set a replacement IP address for a given This option allows applications to set a replacement IP address for a given

View File

@ -252,6 +252,8 @@ Failed to match the pinned key specified with \fICURLOPT_PINNEDPUBLICKEY(3)\fP.
Status returned failure when asked with \fICURLOPT_SSL_VERIFYSTATUS(3)\fP. Status returned failure when asked with \fICURLOPT_SSL_VERIFYSTATUS(3)\fP.
.IP "CURLE_HTTP2_STREAM (92)" .IP "CURLE_HTTP2_STREAM (92)"
Stream error in the HTTP/2 framing layer. Stream error in the HTTP/2 framing layer.
.IP "CURLE_RECURSIVE_API_CALL (93)"
An API function was called from inside a callback.
.IP "CURLE_OBSOLETE*" .IP "CURLE_OBSOLETE*"
These error codes will never be returned. They were used in an old libcurl These error codes will never be returned. They were used in an old libcurl
version and are currently unused. version and are currently unused.
@ -285,6 +287,8 @@ curl_multi_setopt() with unsupported option
.IP "CURLM_ADDED_ALREADY (7)" .IP "CURLM_ADDED_ALREADY (7)"
An easy handle already added to a multi handle was attempted to get added a An easy handle already added to a multi handle was attempted to get added a
second time. (Added in 7.32.1) second time. (Added in 7.32.1)
.IP "CURLM_RECURSIVE_API_CALL (8)"
An API function was called from inside a callback.
.SH "CURLSHcode" .SH "CURLSHcode"
The "share" interface will return a CURLSHcode to indicate when an error has The "share" interface will return a CURLSHcode to indicate when an error has
occurred. Also consider \fIcurl_share_strerror(3)\fP. occurred. Also consider \fIcurl_share_strerror(3)\fP.

View File

@ -101,6 +101,7 @@ CURLE_QUOTE_ERROR 7.17.0
CURLE_RANGE_ERROR 7.17.0 CURLE_RANGE_ERROR 7.17.0
CURLE_READ_ERROR 7.1 CURLE_READ_ERROR 7.1
CURLE_RECV_ERROR 7.10 CURLE_RECV_ERROR 7.10
CURLE_RECURSIVE_API_CALL 7.59.0
CURLE_REMOTE_ACCESS_DENIED 7.17.0 CURLE_REMOTE_ACCESS_DENIED 7.17.0
CURLE_REMOTE_DISK_FULL 7.17.0 CURLE_REMOTE_DISK_FULL 7.17.0
CURLE_REMOTE_FILE_EXISTS 7.17.0 CURLE_REMOTE_FILE_EXISTS 7.17.0
@ -323,6 +324,7 @@ CURLM_CALL_MULTI_SOCKET 7.15.5
CURLM_INTERNAL_ERROR 7.9.6 CURLM_INTERNAL_ERROR 7.9.6
CURLM_OK 7.9.6 CURLM_OK 7.9.6
CURLM_OUT_OF_MEMORY 7.9.6 CURLM_OUT_OF_MEMORY 7.9.6
CURLM_RECURSIVE_API_CALL 7.59.0
CURLM_UNKNOWN_OPTION 7.15.4 CURLM_UNKNOWN_OPTION 7.15.4
CURLOPTTYPE_FUNCTIONPOINT 7.1 CURLOPTTYPE_FUNCTIONPOINT 7.1
CURLOPTTYPE_LONG 7.1 CURLOPTTYPE_LONG 7.1

View File

@ -577,6 +577,8 @@ typedef enum {
CURLE_SSL_INVALIDCERTSTATUS, /* 91 - invalid certificate status */ CURLE_SSL_INVALIDCERTSTATUS, /* 91 - invalid certificate status */
CURLE_HTTP2_STREAM, /* 92 - stream error in HTTP/2 framing layer CURLE_HTTP2_STREAM, /* 92 - stream error in HTTP/2 framing layer
*/ */
CURLE_RECURSIVE_API_CALL, /* 93 - an api function was called from
inside a callback */
CURL_LAST /* never use! */ CURL_LAST /* never use! */
} CURLcode; } CURLcode;

View File

@ -70,6 +70,8 @@ typedef enum {
CURLM_UNKNOWN_OPTION, /* curl_multi_setopt() with unsupported option */ CURLM_UNKNOWN_OPTION, /* curl_multi_setopt() with unsupported option */
CURLM_ADDED_ALREADY, /* an easy handle already added to a multi handle was CURLM_ADDED_ALREADY, /* an easy handle already added to a multi handle was
attempted to get added - again */ attempted to get added - again */
CURLM_RECURSIVE_API_CALL, /* an api function was called from inside a
callback */
CURLM_LAST CURLM_LAST
} CURLMcode; } CURLMcode;

View File

@ -1033,9 +1033,11 @@ static CURLcode singleipconnect(struct connectdata *conn,
if(data->set.fsockopt) { if(data->set.fsockopt) {
/* activate callback for setting socket options */ /* activate callback for setting socket options */
Curl_set_in_callback(data, true);
error = data->set.fsockopt(data->set.sockopt_client, error = data->set.fsockopt(data->set.sockopt_client,
sockfd, sockfd,
CURLSOCKTYPE_IPCXN); CURLSOCKTYPE_IPCXN);
Curl_set_in_callback(data, false);
if(error == CURL_SOCKOPT_ALREADY_CONNECTED) if(error == CURL_SOCKOPT_ALREADY_CONNECTED)
isconnected = TRUE; isconnected = TRUE;
@ -1311,8 +1313,12 @@ int Curl_closesocket(struct connectdata *conn,
status */ status */
conn->sock_accepted[SECONDARYSOCKET] = FALSE; conn->sock_accepted[SECONDARYSOCKET] = FALSE;
else { else {
int rc;
Curl_multi_closed(conn, sock); Curl_multi_closed(conn, sock);
return conn->fclosesocket(conn->closesocket_client, sock); Curl_set_in_callback(conn->data, true);
rc = conn->fclosesocket(conn->closesocket_client, sock);
Curl_set_in_callback(conn->data, false);
return rc;
} }
} }
@ -1363,7 +1369,7 @@ CURLcode Curl_socket(struct connectdata *conn,
addr->addrlen = sizeof(struct Curl_sockaddr_storage); addr->addrlen = sizeof(struct Curl_sockaddr_storage);
memcpy(&addr->sa_addr, ai->ai_addr, addr->addrlen); memcpy(&addr->sa_addr, ai->ai_addr, addr->addrlen);
if(data->set.fopensocket) if(data->set.fopensocket) {
/* /*
* If the opensocket callback is set, all the destination address * If the opensocket callback is set, all the destination address
* information is passed to the callback. Depending on this information the * information is passed to the callback. Depending on this information the
@ -1373,9 +1379,12 @@ CURLcode Curl_socket(struct connectdata *conn,
* might have been changed and this 'new' address will actually be used * might have been changed and this 'new' address will actually be used
* here to connect. * here to connect.
*/ */
Curl_set_in_callback(data, true);
*sockfd = data->set.fopensocket(data->set.opensocket_client, *sockfd = data->set.fopensocket(data->set.opensocket_client,
CURLSOCKTYPE_IPCXN, CURLSOCKTYPE_IPCXN,
(struct curl_sockaddr *)addr); (struct curl_sockaddr *)addr);
Curl_set_in_callback(data, false);
}
else else
/* opensocket callback not set, so simply create the socket now */ /* opensocket callback not set, so simply create the socket now */
*sockfd = socket(addr->family, addr->socktype, addr->protocol); *sockfd = socket(addr->family, addr->socktype, addr->protocol);

View File

@ -61,6 +61,7 @@
#include "strdup.h" #include "strdup.h"
#include "progress.h" #include "progress.h"
#include "easyif.h" #include "easyif.h"
#include "multiif.h"
#include "select.h" #include "select.h"
#include "sendf.h" /* for failf function prototype */ #include "sendf.h" /* for failf function prototype */
#include "connect.h" /* for Curl_getconnectinfo */ #include "connect.h" /* for Curl_getconnectinfo */
@ -761,6 +762,9 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events)
data->multi_easy = multi; data->multi_easy = multi;
} }
if(multi->in_callback)
return CURLE_RECURSIVE_API_CALL;
/* Copy the MAXCONNECTS option to the multi handle */ /* Copy the MAXCONNECTS option to the multi handle */
curl_multi_setopt(multi, CURLMOPT_MAXCONNECTS, data->set.maxconnects); curl_multi_setopt(multi, CURLMOPT_MAXCONNECTS, data->set.maxconnects);
@ -1030,6 +1034,9 @@ void curl_easy_reset(struct Curl_easy *data)
* the pausing, you may get your write callback called at this point. * the pausing, you may get your write callback called at this point.
* *
* Action is a bitmask consisting of CURLPAUSE_* bits in curl/curl.h * Action is a bitmask consisting of CURLPAUSE_* bits in curl/curl.h
*
* NOTE: This is one of few API functions that are allowed to be called from
* within a callback.
*/ */
CURLcode curl_easy_pause(struct Curl_easy *data, int action) CURLcode curl_easy_pause(struct Curl_easy *data, int action)
{ {
@ -1134,6 +1141,9 @@ CURLcode curl_easy_recv(struct Curl_easy *data, void *buffer, size_t buflen,
ssize_t n1; ssize_t n1;
struct connectdata *c; struct connectdata *c;
if(Curl_is_in_callback(data))
return CURLE_RECURSIVE_API_CALL;
result = easy_connection(data, &sfd, &c); result = easy_connection(data, &sfd, &c);
if(result) if(result)
return result; return result;
@ -1161,6 +1171,9 @@ CURLcode curl_easy_send(struct Curl_easy *data, const void *buffer,
ssize_t n1; ssize_t n1;
struct connectdata *c = NULL; struct connectdata *c = NULL;
if(Curl_is_in_callback(data))
return CURLE_RECURSIVE_API_CALL;
result = easy_connection(data, &sfd, &c); result = easy_connection(data, &sfd, &c);
if(result) if(result)
return result; return result;

View File

@ -311,9 +311,11 @@ static CURLcode AcceptServerConnect(struct connectdata *conn)
int error = 0; int error = 0;
/* activate callback for setting socket options */ /* activate callback for setting socket options */
Curl_set_in_callback(data, true);
error = data->set.fsockopt(data->set.sockopt_client, error = data->set.fsockopt(data->set.sockopt_client,
s, s,
CURLSOCKTYPE_ACCEPT); CURLSOCKTYPE_ACCEPT);
Curl_set_in_callback(data, false);
if(error) { if(error) {
close_secondarysocket(conn); close_secondarysocket(conn);
@ -1616,8 +1618,10 @@ static CURLcode ftp_state_ul_setup(struct connectdata *conn,
/* Let's read off the proper amount of bytes from the input. */ /* Let's read off the proper amount of bytes from the input. */
if(conn->seek_func) { if(conn->seek_func) {
Curl_set_in_callback(data, true);
seekerr = conn->seek_func(conn->seek_client, data->state.resume_from, seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
SEEK_SET); SEEK_SET);
Curl_set_in_callback(data, true);
} }
if(seekerr != CURL_SEEKFUNC_OK) { if(seekerr != CURL_SEEKFUNC_OK) {
@ -3181,7 +3185,9 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status,
if(data->state.wildcardmatch) { if(data->state.wildcardmatch) {
if(data->set.chunk_end && ftpc->file) { if(data->set.chunk_end && ftpc->file) {
Curl_set_in_callback(data, true);
data->set.chunk_end(data->wildcard.customptr); data->set.chunk_end(data->wildcard.customptr);
Curl_set_in_callback(data, false);
} }
ftpc->known_filesize = -1; ftpc->known_filesize = -1;
} }
@ -3834,8 +3840,11 @@ static CURLcode wc_statemach(struct connectdata *conn)
infof(conn->data, "Wildcard - START of \"%s\"\n", finfo->filename); infof(conn->data, "Wildcard - START of \"%s\"\n", finfo->filename);
if(conn->data->set.chunk_bgn) { if(conn->data->set.chunk_bgn) {
long userresponse = conn->data->set.chunk_bgn( long userresponse;
Curl_set_in_callback(conn->data, true);
userresponse = conn->data->set.chunk_bgn(
finfo, wildcard->customptr, (int)wildcard->filelist.size); finfo, wildcard->customptr, (int)wildcard->filelist.size);
Curl_set_in_callback(conn->data, false);
switch(userresponse) { switch(userresponse) {
case CURL_CHUNK_BGN_FUNC_SKIP: case CURL_CHUNK_BGN_FUNC_SKIP:
infof(conn->data, "Wildcard - \"%s\" skipped by user\n", infof(conn->data, "Wildcard - \"%s\" skipped by user\n",
@ -3871,8 +3880,11 @@ static CURLcode wc_statemach(struct connectdata *conn)
} break; } break;
case CURLWC_SKIP: { case CURLWC_SKIP: {
if(conn->data->set.chunk_end) if(conn->data->set.chunk_end) {
Curl_set_in_callback(conn->data, true);
conn->data->set.chunk_end(conn->data->wildcard.customptr); conn->data->set.chunk_end(conn->data->wildcard.customptr);
Curl_set_in_callback(conn->data, false);
}
Curl_llist_remove(&wildcard->filelist, wildcard->filelist.head, NULL); Curl_llist_remove(&wildcard->filelist, wildcard->filelist.head, NULL);
wildcard->state = (wildcard->filelist.size == 0) ? wildcard->state = (wildcard->filelist.size == 0) ?
CURLWC_CLEAN : CURLWC_DOWNLOADING; CURLWC_CLEAN : CURLWC_DOWNLOADING;

View File

@ -49,6 +49,7 @@
#include "ftplistparser.h" #include "ftplistparser.h"
#include "curl_fnmatch.h" #include "curl_fnmatch.h"
#include "curl_memory.h" #include "curl_memory.h"
#include "multiif.h"
/* The last #include file should be: */ /* The last #include file should be: */
#include "memdebug.h" #include "memdebug.h"
@ -294,6 +295,7 @@ static CURLcode ftp_pl_insert_finfo(struct connectdata *conn,
compare = Curl_fnmatch; compare = Curl_fnmatch;
/* filter pattern-corresponding filenames */ /* filter pattern-corresponding filenames */
Curl_set_in_callback(conn->data, true);
if(compare(conn->data->set.fnmatch_data, wc->pattern, if(compare(conn->data->set.fnmatch_data, wc->pattern,
finfo->filename) == 0) { finfo->filename) == 0) {
/* discard symlink which is containing multiple " -> " */ /* discard symlink which is containing multiple " -> " */
@ -305,6 +307,7 @@ static CURLcode ftp_pl_insert_finfo(struct connectdata *conn,
else { else {
add = FALSE; add = FALSE;
} }
Curl_set_in_callback(conn->data, false);
if(add) { if(add) {
Curl_llist_insert_next(llist, llist->tail, finfo, &infop->list); Curl_llist_insert_next(llist, llist->tail, finfo, &infop->list);

View File

@ -2191,8 +2191,10 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
/* Now, let's read off the proper amount of bytes from the /* Now, let's read off the proper amount of bytes from the
input. */ input. */
if(conn->seek_func) { if(conn->seek_func) {
Curl_set_in_callback(data, true);
seekerr = conn->seek_func(conn->seek_client, data->state.resume_from, seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
SEEK_SET); SEEK_SET);
Curl_set_in_callback(data, false);
} }
if(seekerr != CURL_SEEKFUNC_OK) { if(seekerr != CURL_SEEKFUNC_OK) {

View File

@ -464,9 +464,11 @@ static int push_promise(struct Curl_easy *data,
goto fail; goto fail;
} }
Curl_set_in_callback(data, true);
rv = data->multi->push_cb(data, newhandle, rv = data->multi->push_cb(data, newhandle,
stream->push_headers_used, &heads, stream->push_headers_used, &heads,
data->multi->push_userp); data->multi->push_userp);
Curl_set_in_callback(data, false);
/* free the headers again */ /* free the headers again */
for(i = 0; i<stream->push_headers_used; i++) for(i = 0; i<stream->push_headers_used; i++)

View File

@ -366,6 +366,9 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi,
if(data->multi) if(data->multi)
return CURLM_ADDED_ALREADY; return CURLM_ADDED_ALREADY;
if(multi->in_callback)
return CURLM_RECURSIVE_API_CALL;
/* Initialize timeout list for this handle */ /* Initialize timeout list for this handle */
Curl_llist_init(&data->state.timeoutlist, NULL); Curl_llist_init(&data->state.timeoutlist, NULL);
@ -640,6 +643,9 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi,
if(!data->multi) if(!data->multi)
return CURLM_OK; /* it is already removed so let's say it is fine! */ return CURLM_OK; /* it is already removed so let's say it is fine! */
if(multi->in_callback)
return CURLM_RECURSIVE_API_CALL;
premature = (data->mstate < CURLM_STATE_COMPLETED) ? TRUE : FALSE; premature = (data->mstate < CURLM_STATE_COMPLETED) ? TRUE : FALSE;
easy_owns_conn = (data->easy_conn && (data->easy_conn->data == easy)) ? easy_owns_conn = (data->easy_conn && (data->easy_conn->data == easy)) ?
TRUE : FALSE; TRUE : FALSE;
@ -903,6 +909,9 @@ CURLMcode curl_multi_fdset(struct Curl_multi *multi,
if(!GOOD_MULTI_HANDLE(multi)) if(!GOOD_MULTI_HANDLE(multi))
return CURLM_BAD_HANDLE; return CURLM_BAD_HANDLE;
if(multi->in_callback)
return CURLM_RECURSIVE_API_CALL;
data = multi->easyp; data = multi->easyp;
while(data) { while(data) {
bitmap = multi_getsock(data, sockbunch, MAX_SOCKSPEREASYHANDLE); bitmap = multi_getsock(data, sockbunch, MAX_SOCKSPEREASYHANDLE);
@ -956,6 +965,9 @@ CURLMcode curl_multi_wait(struct Curl_multi *multi,
if(!GOOD_MULTI_HANDLE(multi)) if(!GOOD_MULTI_HANDLE(multi))
return CURLM_BAD_HANDLE; return CURLM_BAD_HANDLE;
if(multi->in_callback)
return CURLM_RECURSIVE_API_CALL;
/* If the internally desired timeout is actually shorter than requested from /* If the internally desired timeout is actually shorter than requested from
the outside, then use the shorter time! But only if the internal timer the outside, then use the shorter time! But only if the internal timer
is actually larger than -1! */ is actually larger than -1! */
@ -1121,6 +1133,9 @@ CURLMcode Curl_multi_add_perform(struct Curl_multi *multi,
{ {
CURLMcode rc; CURLMcode rc;
if(multi->in_callback)
return CURLM_RECURSIVE_API_CALL;
rc = curl_multi_add_handle(multi, data); rc = curl_multi_add_handle(multi, data);
if(!rc) { if(!rc) {
struct SingleRequest *k = &data->req; struct SingleRequest *k = &data->req;
@ -2127,6 +2142,9 @@ CURLMcode curl_multi_perform(struct Curl_multi *multi, int *running_handles)
if(!GOOD_MULTI_HANDLE(multi)) if(!GOOD_MULTI_HANDLE(multi))
return CURLM_BAD_HANDLE; return CURLM_BAD_HANDLE;
if(multi->in_callback)
return CURLM_RECURSIVE_API_CALL;
data = multi->easyp; data = multi->easyp;
while(data) { while(data) {
CURLMcode result; CURLMcode result;
@ -2174,6 +2192,9 @@ CURLMcode curl_multi_cleanup(struct Curl_multi *multi)
struct Curl_easy *nextdata; struct Curl_easy *nextdata;
if(GOOD_MULTI_HANDLE(multi)) { if(GOOD_MULTI_HANDLE(multi)) {
if(multi->in_callback)
return CURLM_RECURSIVE_API_CALL;
multi->type = 0; /* not good anymore */ multi->type = 0; /* not good anymore */
/* Firsrt remove all remaining easy handles */ /* Firsrt remove all remaining easy handles */
@ -2234,7 +2255,9 @@ CURLMsg *curl_multi_info_read(struct Curl_multi *multi, int *msgs_in_queue)
*msgs_in_queue = 0; /* default to none */ *msgs_in_queue = 0; /* default to none */
if(GOOD_MULTI_HANDLE(multi) && Curl_llist_count(&multi->msglist)) { if(GOOD_MULTI_HANDLE(multi) &&
!multi->in_callback &&
Curl_llist_count(&multi->msglist)) {
/* there is one or more messages in the list */ /* there is one or more messages in the list */
struct curl_llist_element *e; struct curl_llist_element *e;
@ -2624,6 +2647,9 @@ CURLMcode curl_multi_setopt(struct Curl_multi *multi,
if(!GOOD_MULTI_HANDLE(multi)) if(!GOOD_MULTI_HANDLE(multi))
return CURLM_BAD_HANDLE; return CURLM_BAD_HANDLE;
if(multi->in_callback)
return CURLM_RECURSIVE_API_CALL;
va_start(param, option); va_start(param, option);
switch(option) { switch(option) {
@ -2688,7 +2714,10 @@ CURLMcode curl_multi_setopt(struct Curl_multi *multi,
CURLMcode curl_multi_socket(struct Curl_multi *multi, curl_socket_t s, CURLMcode curl_multi_socket(struct Curl_multi *multi, curl_socket_t s,
int *running_handles) int *running_handles)
{ {
CURLMcode result = multi_socket(multi, FALSE, s, 0, running_handles); CURLMcode result;
if(multi->in_callback)
return CURLM_RECURSIVE_API_CALL;
result = multi_socket(multi, FALSE, s, 0, running_handles);
if(CURLM_OK >= result) if(CURLM_OK >= result)
update_timer(multi); update_timer(multi);
return result; return result;
@ -2697,8 +2726,10 @@ CURLMcode curl_multi_socket(struct Curl_multi *multi, curl_socket_t s,
CURLMcode curl_multi_socket_action(struct Curl_multi *multi, curl_socket_t s, CURLMcode curl_multi_socket_action(struct Curl_multi *multi, curl_socket_t s,
int ev_bitmask, int *running_handles) int ev_bitmask, int *running_handles)
{ {
CURLMcode result = multi_socket(multi, FALSE, s, CURLMcode result;
ev_bitmask, running_handles); if(multi->in_callback)
return CURLM_RECURSIVE_API_CALL;
result = multi_socket(multi, FALSE, s, ev_bitmask, running_handles);
if(CURLM_OK >= result) if(CURLM_OK >= result)
update_timer(multi); update_timer(multi);
return result; return result;
@ -2707,8 +2738,10 @@ CURLMcode curl_multi_socket_action(struct Curl_multi *multi, curl_socket_t s,
CURLMcode curl_multi_socket_all(struct Curl_multi *multi, int *running_handles) CURLMcode curl_multi_socket_all(struct Curl_multi *multi, int *running_handles)
{ {
CURLMcode result = multi_socket(multi, TRUE, CURL_SOCKET_BAD, 0, CURLMcode result;
running_handles); if(multi->in_callback)
return CURLM_RECURSIVE_API_CALL;
result = multi_socket(multi, TRUE, CURL_SOCKET_BAD, 0, running_handles);
if(CURLM_OK >= result) if(CURLM_OK >= result)
update_timer(multi); update_timer(multi);
return result; return result;
@ -2760,6 +2793,9 @@ CURLMcode curl_multi_timeout(struct Curl_multi *multi,
if(!GOOD_MULTI_HANDLE(multi)) if(!GOOD_MULTI_HANDLE(multi))
return CURLM_BAD_HANDLE; return CURLM_BAD_HANDLE;
if(multi->in_callback)
return CURLM_RECURSIVE_API_CALL;
return multi_timeout(multi, timeout_ms); return multi_timeout(multi, timeout_ms);
} }
@ -2992,6 +3028,9 @@ CURLMcode curl_multi_assign(struct Curl_multi *multi, curl_socket_t s,
{ {
struct Curl_sh_entry *there = NULL; struct Curl_sh_entry *there = NULL;
if(multi->in_callback)
return CURLM_RECURSIVE_API_CALL;
there = sh_getentry(&multi->sockhash, s); there = sh_getentry(&multi->sockhash, s);
if(!there) if(!there)
@ -3054,6 +3093,20 @@ void Curl_multi_process_pending_handles(struct Curl_multi *multi)
} }
} }
void Curl_set_in_callback(struct Curl_easy *easy, bool value)
{
if(easy->multi_easy)
easy->multi_easy->in_callback = value;
else if(easy->multi)
easy->multi->in_callback = value;
}
bool Curl_is_in_callback(struct Curl_easy *easy)
{
return ((easy->multi && easy->multi->in_callback) ||
(easy->multi_easy && easy->multi_easy->in_callback));
}
#ifdef DEBUGBUILD #ifdef DEBUGBUILD
void Curl_multi_dump(struct Curl_multi *multi) void Curl_multi_dump(struct Curl_multi *multi)
{ {

View File

@ -146,6 +146,7 @@ struct Curl_multi {
void *timer_userp; void *timer_userp;
struct curltime timer_lastcall; /* the fixed time for the timeout for the struct curltime timer_lastcall; /* the fixed time for the timeout for the
previous callback */ previous callback */
bool in_callback; /* true while executing a callback */
}; };
#endif /* HEADER_CURL_MULTIHANDLE_H */ #endif /* HEADER_CURL_MULTIHANDLE_H */

View File

@ -31,6 +31,8 @@ void Curl_expire_clear(struct Curl_easy *data);
void Curl_expire_done(struct Curl_easy *data, expire_id id); void Curl_expire_done(struct Curl_easy *data, expire_id id);
bool Curl_pipeline_wanted(const struct Curl_multi* multi, int bits); bool Curl_pipeline_wanted(const struct Curl_multi* multi, int bits);
void Curl_multi_handlePipeBreak(struct Curl_easy *data); void Curl_multi_handlePipeBreak(struct Curl_easy *data);
void Curl_set_in_callback(struct Curl_easy *data, bool value);
bool Curl_is_in_callback(struct Curl_easy *easy);
/* Internal version of curl_multi_init() accepts size parameters for the /* Internal version of curl_multi_init() accepts size parameters for the
socket and connection hashes */ socket and connection hashes */

View File

@ -84,7 +84,10 @@ CURLcode Curl_convert_to_network(struct Curl_easy *data,
{ {
if(data && data->set.convtonetwork) { if(data && data->set.convtonetwork) {
/* use translation callback */ /* use translation callback */
CURLcode result = data->set.convtonetwork(buffer, length); CURLcode result;
Curl_set_in_callback(data, true);
result = data->set.convtonetwork(buffer, length);
Curl_set_in_callback(data, false);
if(result) { if(result) {
failf(data, failf(data,
"CURLOPT_CONV_TO_NETWORK_FUNCTION callback returned %d: %s", "CURLOPT_CONV_TO_NETWORK_FUNCTION callback returned %d: %s",
@ -147,7 +150,10 @@ CURLcode Curl_convert_from_network(struct Curl_easy *data,
{ {
if(data && data->set.convfromnetwork) { if(data && data->set.convfromnetwork) {
/* use translation callback */ /* use translation callback */
CURLcode result = data->set.convfromnetwork(buffer, length); CURLcode result;
Curl_set_in_callback(data, true);
result = data->set.convfromnetwork(buffer, length);
Curl_set_in_callback(data, false);
if(result) { if(result) {
failf(data, failf(data,
"CURLOPT_CONV_FROM_NETWORK_FUNCTION callback returned %d: %s", "CURLOPT_CONV_FROM_NETWORK_FUNCTION callback returned %d: %s",
@ -210,7 +216,10 @@ CURLcode Curl_convert_from_utf8(struct Curl_easy *data,
{ {
if(data && data->set.convfromutf8) { if(data && data->set.convfromutf8) {
/* use translation callback */ /* use translation callback */
CURLcode result = data->set.convfromutf8(buffer, length); CURLcode result;
Curl_set_in_callback(data, true);
result = data->set.convfromutf8(buffer, length);
Curl_set_in_callback(data, false);
if(result) { if(result) {
failf(data, failf(data,
"CURLOPT_CONV_FROM_UTF8_FUNCTION callback returned %d: %s", "CURLOPT_CONV_FROM_UTF8_FUNCTION callback returned %d: %s",

View File

@ -24,6 +24,7 @@
#include "urldata.h" #include "urldata.h"
#include "sendf.h" #include "sendf.h"
#include "multiif.h"
#include "progress.h" #include "progress.h"
#include "curl_printf.h" #include "curl_printf.h"
@ -461,22 +462,26 @@ int Curl_pgrsUpdate(struct connectdata *conn)
if(data->set.fxferinfo) { if(data->set.fxferinfo) {
/* There's a callback set, call that */ /* There's a callback set, call that */
Curl_set_in_callback(data, true);
result = data->set.fxferinfo(data->set.progress_client, result = data->set.fxferinfo(data->set.progress_client,
data->progress.size_dl, data->progress.size_dl,
data->progress.downloaded, data->progress.downloaded,
data->progress.size_ul, data->progress.size_ul,
data->progress.uploaded); data->progress.uploaded);
Curl_set_in_callback(data, false);
if(result) if(result)
failf(data, "Callback aborted"); failf(data, "Callback aborted");
return result; return result;
} }
if(data->set.fprogress) { if(data->set.fprogress) {
/* The older deprecated callback is set, call that */ /* The older deprecated callback is set, call that */
Curl_set_in_callback(data, true);
result = data->set.fprogress(data->set.progress_client, result = data->set.fprogress(data->set.progress_client,
(double)data->progress.size_dl, (double)data->progress.size_dl,
(double)data->progress.downloaded, (double)data->progress.downloaded,
(double)data->progress.size_ul, (double)data->progress.size_ul,
(double)data->progress.uploaded); (double)data->progress.uploaded);
Curl_set_in_callback(data, false);
if(result) if(result)
failf(data, "Callback aborted"); failf(data, "Callback aborted");
return result; return result;

View File

@ -770,7 +770,9 @@ CURLcode rtp_client_write(struct connectdata *conn, char *ptr, size_t len)
user_ptr = data->set.out; user_ptr = data->set.out;
} }
Curl_set_in_callback(data, true);
wrote = writeit(ptr, 1, len, user_ptr); wrote = writeit(ptr, 1, len, user_ptr);
Curl_set_in_callback(data, false);
if(CURL_WRITEFUNC_PAUSE == wrote) { if(CURL_WRITEFUNC_PAUSE == wrote) {
failf(data, "Cannot pause RTP"); failf(data, "Cannot pause RTP");

View File

@ -37,6 +37,7 @@
#include "connect.h" #include "connect.h"
#include "vtls/vtls.h" #include "vtls/vtls.h"
#include "ssh.h" #include "ssh.h"
#include "easyif.h"
#include "multiif.h" #include "multiif.h"
#include "non-ascii.h" #include "non-ascii.h"
#include "strerror.h" #include "strerror.h"
@ -598,7 +599,10 @@ CURLcode Curl_client_chop_write(struct connectdata *conn,
} }
if(writeheader) { if(writeheader) {
size_t wrote = writeheader(ptr, 1, chunklen, data->set.writeheader); size_t wrote;
Curl_set_in_callback(data, true);
wrote = writeheader(ptr, 1, chunklen, data->set.writeheader);
Curl_set_in_callback(data, false);
if(CURL_WRITEFUNC_PAUSE == wrote) if(CURL_WRITEFUNC_PAUSE == wrote)
/* here we pass in the HEADER bit only since if this was body as well /* here we pass in the HEADER bit only since if this was body as well
@ -798,8 +802,11 @@ static int showit(struct Curl_easy *data, curl_infotype type,
} }
#endif /* CURL_DOES_CONVERSIONS */ #endif /* CURL_DOES_CONVERSIONS */
if(data->set.fdebug) if(data->set.fdebug) {
Curl_set_in_callback(data, true);
rc = (*data->set.fdebug)(data, type, ptr, size, data->set.debugdata); rc = (*data->set.fdebug)(data, type, ptr, size, data->set.debugdata);
Curl_set_in_callback(data, false);
}
else { else {
switch(type) { switch(type) {
case CURLINFO_TEXT: case CURLINFO_TEXT:

View File

@ -43,6 +43,7 @@
#include "sendf.h" #include "sendf.h"
#include "http2.h" #include "http2.h"
#include "setopt.h" #include "setopt.h"
#include "multiif.h"
/* The last 3 #include files should be in this order */ /* The last 3 #include files should be in this order */
#include "curl_printf.h" #include "curl_printf.h"
@ -2544,6 +2545,9 @@ CURLcode Curl_vsetopt(struct Curl_easy *data, CURLoption option,
/* /*
* curl_easy_setopt() is the external interface for setting options on an * curl_easy_setopt() is the external interface for setting options on an
* easy handle. * easy handle.
*
* NOTE: This is one of few API functions that are allowed to be called from
* within a callback.
*/ */
#undef curl_easy_setopt #undef curl_easy_setopt

View File

@ -383,8 +383,10 @@ static int myssh_is_known(struct connectdata *conn)
} }
/* we don't have anything equivalent to knownkey. Always NULL */ /* we don't have anything equivalent to knownkey. Always NULL */
Curl_set_in_callback(data, true);
rc = func(data, NULL, &foundkey, /* from the remote host */ rc = func(data, NULL, &foundkey, /* from the remote host */
keymatch, data->set.ssh_keyfunc_userp); keymatch, data->set.ssh_keyfunc_userp);
Curl_set_in_callback(data, false);
switch(rc) { switch(rc) {
case CURLKHSTAT_FINE_ADD_TO_FILE: case CURLKHSTAT_FINE_ADD_TO_FILE:
@ -1128,8 +1130,10 @@ static CURLcode myssh_statemach_act(struct connectdata *conn, bool *block)
if(data->state.resume_from > 0) { if(data->state.resume_from > 0) {
/* Let's read off the proper amount of bytes from the input. */ /* Let's read off the proper amount of bytes from the input. */
if(conn->seek_func) { if(conn->seek_func) {
Curl_set_in_callback(data, true);
seekerr = conn->seek_func(conn->seek_client, data->state.resume_from, seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
SEEK_SET); SEEK_SET);
Curl_set_in_callback(data, false);
} }
if(seekerr != CURL_SEEKFUNC_OK) { if(seekerr != CURL_SEEKFUNC_OK) {

View File

@ -523,9 +523,11 @@ static CURLcode ssh_knownhost(struct connectdata *conn)
keymatch = (enum curl_khmatch)keycheck; keymatch = (enum curl_khmatch)keycheck;
/* Ask the callback how to behave */ /* Ask the callback how to behave */
Curl_set_in_callback(data, true);
rc = func(data, knownkeyp, /* from the knownhosts file */ rc = func(data, knownkeyp, /* from the knownhosts file */
&foundkey, /* from the remote host */ &foundkey, /* from the remote host */
keymatch, data->set.ssh_keyfunc_userp); keymatch, data->set.ssh_keyfunc_userp);
Curl_set_in_callback(data, false);
} }
else else
/* no remotekey means failure! */ /* no remotekey means failure! */
@ -1747,8 +1749,10 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
if(data->state.resume_from > 0) { if(data->state.resume_from > 0) {
/* Let's read off the proper amount of bytes from the input. */ /* Let's read off the proper amount of bytes from the input. */
if(conn->seek_func) { if(conn->seek_func) {
Curl_set_in_callback(data, true);
seekerr = conn->seek_func(conn->seek_client, data->state.resume_from, seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
SEEK_SET); SEEK_SET);
Curl_set_in_callback(data, false);
} }
if(seekerr != CURL_SEEKFUNC_OK) { if(seekerr != CURL_SEEKFUNC_OK) {
@ -1765,9 +1769,12 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
(size_t)data->set.buffer_size : (size_t)data->set.buffer_size :
curlx_sotouz(data->state.resume_from - passed); curlx_sotouz(data->state.resume_from - passed);
size_t actuallyread = size_t actuallyread;
data->state.fread_func(data->state.buffer, 1, Curl_set_in_callback(data, true);
readthisamountnow, data->state.in); actuallyread = data->state.fread_func(data->state.buffer, 1,
readthisamountnow,
data->state.in);
Curl_set_in_callback(data, false);
passed += actuallyread; passed += actuallyread;
if((actuallyread == 0) || (actuallyread > readthisamountnow)) { if((actuallyread == 0) || (actuallyread > readthisamountnow)) {

View File

@ -312,6 +312,9 @@ curl_easy_strerror(CURLcode error)
case CURLE_HTTP2_STREAM: case CURLE_HTTP2_STREAM:
return "Stream error in the HTTP/2 framing layer"; return "Stream error in the HTTP/2 framing layer";
case CURLE_RECURSIVE_API_CALL:
return "API function called from within callback";
/* error codes not used by current libcurl */ /* error codes not used by current libcurl */
case CURLE_OBSOLETE20: case CURLE_OBSOLETE20:
case CURLE_OBSOLETE24: case CURLE_OBSOLETE24:
@ -380,6 +383,9 @@ curl_multi_strerror(CURLMcode error)
case CURLM_ADDED_ALREADY: case CURLM_ADDED_ALREADY:
return "The easy handle is already added to a multi handle"; return "The easy handle is already added to a multi handle";
case CURLM_RECURSIVE_API_CALL:
return "API function called from within callback";
case CURLM_LAST: case CURLM_LAST:
break; break;
} }

View File

@ -135,8 +135,10 @@ CURLcode Curl_fillreadbuffer(struct connectdata *conn, int bytes, int *nreadp)
/* this function returns a size_t, so we typecast to int to prevent warnings /* this function returns a size_t, so we typecast to int to prevent warnings
with picky compilers */ with picky compilers */
Curl_set_in_callback(data, true);
nread = (int)data->state.fread_func(data->req.upload_fromhere, 1, nread = (int)data->state.fread_func(data->req.upload_fromhere, 1,
buffersize, data->state.in); buffersize, data->state.in);
Curl_set_in_callback(data, false);
if(nread == CURL_READFUNC_ABORT) { if(nread == CURL_READFUNC_ABORT) {
failf(data, "operation aborted by callback"); failf(data, "operation aborted by callback");
@ -302,7 +304,9 @@ CURLcode Curl_readrewind(struct connectdata *conn)
if(data->set.seek_func) { if(data->set.seek_func) {
int err; int err;
Curl_set_in_callback(data, true);
err = (data->set.seek_func)(data->set.seek_client, 0, SEEK_SET); err = (data->set.seek_func)(data->set.seek_client, 0, SEEK_SET);
Curl_set_in_callback(data, false);
if(err) { if(err) {
failf(data, "seek callback returned error %d", (int)err); failf(data, "seek callback returned error %d", (int)err);
return CURLE_SEND_FAIL_REWIND; return CURLE_SEND_FAIL_REWIND;
@ -311,8 +315,10 @@ CURLcode Curl_readrewind(struct connectdata *conn)
else if(data->set.ioctl_func) { else if(data->set.ioctl_func) {
curlioerr err; curlioerr err;
Curl_set_in_callback(data, true);
err = (data->set.ioctl_func)(data, CURLIOCMD_RESTARTREAD, err = (data->set.ioctl_func)(data, CURLIOCMD_RESTARTREAD,
data->set.ioctl_client); data->set.ioctl_client);
Curl_set_in_callback(data, false);
infof(data, "the ioctl callback returned %d\n", (int)err); infof(data, "the ioctl callback returned %d\n", (int)err);
if(err) { if(err) {

View File

@ -171,7 +171,7 @@ test1520 test1521 \
test1525 test1526 test1527 test1528 test1529 test1530 test1531 test1532 \ test1525 test1526 test1527 test1528 test1529 test1530 test1531 test1532 \
test1533 test1534 test1535 test1536 test1537 test1538 \ test1533 test1534 test1535 test1536 test1537 test1538 \
test1540 \ test1540 \
test1550 test1551 test1552 test1553 test1554 \ test1550 test1551 test1552 test1553 test1554 test1555 \
test1600 test1601 test1602 test1603 test1604 test1605 test1606 \ test1600 test1601 test1602 test1603 test1604 test1605 test1606 \
\ \
test1700 test1701 test1702 \ test1700 test1701 test1702 \

View File

@ -125,7 +125,8 @@ e89: The max connection limit is reached
e90: SSL public key does not match pinned public key e90: SSL public key does not match pinned public key
e91: SSL server certificate status verification FAILED e91: SSL server certificate status verification FAILED
e92: Stream error in the HTTP/2 framing layer e92: Stream error in the HTTP/2 framing layer
e93: Unknown error e93: API function called from within callback
e94: Unknown error
m-1: Please call curl_multi_perform() soon m-1: Please call curl_multi_perform() soon
m0: No error m0: No error
m1: Invalid multi handle m1: Invalid multi handle
@ -135,7 +136,8 @@ m4: Internal error
m5: Invalid socket argument m5: Invalid socket argument
m6: Unknown option m6: Unknown option
m7: The easy handle is already added to a multi handle m7: The easy handle is already added to a multi handle
m8: Unknown error m8: API function called from within callback
m9: Unknown error
s0: No error s0: No error
s1: Unknown share option s1: Unknown share option
s2: Share currently in use s2: Share currently in use

50
tests/data/test1555 Normal file
View File

@ -0,0 +1,50 @@
<testcase>
<info>
<keywords>
RECURSIVE_API_CALL
</keywords>
</info>
# Server-side
<reply>
<data nocheck="yes">
HTTP/1.1 204 PARTIAL
X-Comment: partial response to keep the client waiting
</data>
<postcmd>
wait 10
</postcmd>
</reply>
# Client-side
<client>
<server>
http
</server>
<tool>
lib1555
</tool>
<name>
verify api is protected against calls from callbacks
</name>
# this server/host won't be used for real
<command>
http://%HOSTIP:%HTTPPORT/1555
</command>
</client>
# Verify data after the test has been "shot"
<verify>
<protocol>
</protocol>
# 42 == CURLE_ABORTED_BY_CALLBACK
<errorcode>
42
</errorcode>
<stdout>
curl_easy_recv returned 93
curl_easy_send returned 93
</stdout>
</verify>
</testcase>

View File

@ -27,7 +27,7 @@ noinst_PROGRAMS = chkhostname libauthretry libntlmconnect \
lib1525 lib1526 lib1527 lib1528 lib1529 lib1530 lib1531 lib1532 lib1533 \ lib1525 lib1526 lib1527 lib1528 lib1529 lib1530 lib1531 lib1532 lib1533 \
lib1534 lib1535 lib1536 lib1537 lib1538 \ lib1534 lib1535 lib1536 lib1537 lib1538 \
lib1540 \ lib1540 \
lib1550 lib1551 lib1552 lib1553 lib1554 \ lib1550 lib1551 lib1552 lib1553 lib1554 lib1555 \
lib1900 \ lib1900 \
lib2033 lib2033
@ -477,6 +477,10 @@ lib1553_CPPFLAGS = $(AM_CPPFLAGS)
lib1554_SOURCES = lib1554.c $(SUPPORTFILES) lib1554_SOURCES = lib1554.c $(SUPPORTFILES)
lib1554_CPPFLAGS = $(AM_CPPFLAGS) lib1554_CPPFLAGS = $(AM_CPPFLAGS)
lib1555_SOURCES = lib1555.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS)
lib1555_LDADD = $(TESTUTIL_LIBS)
lib1555_CPPFLAGS = $(AM_CPPFLAGS) -DLIB1555
lib1900_SOURCES = lib1900.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS) lib1900_SOURCES = lib1900.c $(SUPPORTFILES) $(TESTUTIL) $(WARNLESS)
lib1900_LDADD = $(TESTUTIL_LIBS) lib1900_LDADD = $(TESTUTIL_LIBS)
lib1900_CPPFLAGS = $(AM_CPPFLAGS) lib1900_CPPFLAGS = $(AM_CPPFLAGS)

77
tests/libtest/lib1555.c Normal file
View File

@ -0,0 +1,77 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2015, 2017, 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.haxx.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.
*
***************************************************************************/
/*
* Verify that some API functions are locked from being called inside callback
*/
#include "test.h"
#include "memdebug.h"
static CURL *curl;
static int progressCallback(void *arg,
double dltotal,
double dlnow,
double ultotal,
double ulnow)
{
CURLcode res = 0;
(void)arg;
(void)dltotal;
(void)dlnow;
(void)ultotal;
(void)ulnow;
res = curl_easy_recv(curl, NULL, 0, NULL);
printf("curl_easy_recv returned %d\n", res);
res = curl_easy_send(curl, NULL, 0, NULL);
printf("curl_easy_send returned %d\n", res);
return 1;
}
int test(char *URL)
{
int res = 0;
global_init(CURL_GLOBAL_ALL);
easy_init(curl);
easy_setopt(curl, CURLOPT_URL, URL);
easy_setopt(curl, CURLOPT_TIMEOUT, (long)7);
easy_setopt(curl, CURLOPT_NOSIGNAL, (long)1);
easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, progressCallback);
easy_setopt(curl, CURLOPT_PROGRESSDATA, NULL);
easy_setopt(curl, CURLOPT_NOPROGRESS, (long)0);
res = curl_easy_perform(curl);
test_cleanup:
/* undocumented cleanup sequence - type UA */
curl_easy_cleanup(curl);
curl_global_cleanup();
return res;
}