summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/TODO7
-rw-r--r--docs/libcurl/libcurl-errors.34
-rw-r--r--docs/libcurl/symbols-in-versions2
-rw-r--r--include/curl/curl.h2
-rw-r--r--include/curl/multi.h2
-rw-r--r--lib/connect.c13
-rw-r--r--lib/easy.c13
-rw-r--r--lib/ftp.c16
-rw-r--r--lib/ftplistparser.c3
-rw-r--r--lib/http.c2
-rw-r--r--lib/http2.c2
-rw-r--r--lib/multi.c65
-rw-r--r--lib/multihandle.h1
-rw-r--r--lib/multiif.h2
-rw-r--r--lib/non-ascii.c15
-rw-r--r--lib/progress.c5
-rw-r--r--lib/rtsp.c2
-rw-r--r--lib/sendf.c11
-rw-r--r--lib/setopt.c4
-rw-r--r--lib/ssh-libssh.c4
-rw-r--r--lib/ssh.c13
-rw-r--r--lib/strerror.c6
-rw-r--r--lib/transfer.c6
-rw-r--r--tests/data/Makefile.inc2
-rw-r--r--tests/data/test15386
-rw-r--r--tests/data/test155550
-rw-r--r--tests/libtest/Makefile.inc6
-rw-r--r--tests/libtest/lib1555.c77
28 files changed, 312 insertions, 29 deletions
diff --git a/docs/TODO b/docs/TODO
index ecea1eeb1..c06a9ceea 100644
--- a/docs/TODO
+++ b/docs/TODO
@@ -22,7 +22,6 @@
1.4 signal-based resolver timeouts
1.5 get rid of PATH_MAX
1.6 Modified buffer size approach
- 1.7 Detect when called from within callbacks
1.8 CURLOPT_RESOLVE for any port number
1.9 Cache negative name resolves
1.10 auto-detect proxy
@@ -239,12 +238,6 @@
Dynamically allocate buffer size depending on protocol in use in combination
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
This option allows applications to set a replacement IP address for a given
diff --git a/docs/libcurl/libcurl-errors.3 b/docs/libcurl/libcurl-errors.3
index 1b6e34f51..2c866b0d2 100644
--- a/docs/libcurl/libcurl-errors.3
+++ b/docs/libcurl/libcurl-errors.3
@@ -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.
.IP "CURLE_HTTP2_STREAM (92)"
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*"
These error codes will never be returned. They were used in an old libcurl
version and are currently unused.
@@ -285,6 +287,8 @@ curl_multi_setopt() with unsupported option
.IP "CURLM_ADDED_ALREADY (7)"
An easy handle already added to a multi handle was attempted to get added a
second time. (Added in 7.32.1)
+.IP "CURLM_RECURSIVE_API_CALL (8)"
+An API function was called from inside a callback.
.SH "CURLSHcode"
The "share" interface will return a CURLSHcode to indicate when an error has
occurred. Also consider \fIcurl_share_strerror(3)\fP.
diff --git a/docs/libcurl/symbols-in-versions b/docs/libcurl/symbols-in-versions
index 306b7a845..ac3dadee3 100644
--- a/docs/libcurl/symbols-in-versions
+++ b/docs/libcurl/symbols-in-versions
@@ -101,6 +101,7 @@ CURLE_QUOTE_ERROR 7.17.0
CURLE_RANGE_ERROR 7.17.0
CURLE_READ_ERROR 7.1
CURLE_RECV_ERROR 7.10
+CURLE_RECURSIVE_API_CALL 7.59.0
CURLE_REMOTE_ACCESS_DENIED 7.17.0
CURLE_REMOTE_DISK_FULL 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_OK 7.9.6
CURLM_OUT_OF_MEMORY 7.9.6
+CURLM_RECURSIVE_API_CALL 7.59.0
CURLM_UNKNOWN_OPTION 7.15.4
CURLOPTTYPE_FUNCTIONPOINT 7.1
CURLOPTTYPE_LONG 7.1
diff --git a/include/curl/curl.h b/include/curl/curl.h
index 69283eb08..7cab0a16c 100644
--- a/include/curl/curl.h
+++ b/include/curl/curl.h
@@ -577,6 +577,8 @@ typedef enum {
CURLE_SSL_INVALIDCERTSTATUS, /* 91 - invalid certificate status */
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! */
} CURLcode;
diff --git a/include/curl/multi.h b/include/curl/multi.h
index 911c91dd1..bd106ae85 100644
--- a/include/curl/multi.h
+++ b/include/curl/multi.h
@@ -70,6 +70,8 @@ typedef enum {
CURLM_UNKNOWN_OPTION, /* curl_multi_setopt() with unsupported option */
CURLM_ADDED_ALREADY, /* an easy handle already added to a multi handle was
attempted to get added - again */
+ CURLM_RECURSIVE_API_CALL, /* an api function was called from inside a
+ callback */
CURLM_LAST
} CURLMcode;
diff --git a/lib/connect.c b/lib/connect.c
index 3edb71eb7..d56cf2f16 100644
--- a/lib/connect.c
+++ b/lib/connect.c
@@ -1033,9 +1033,11 @@ static CURLcode singleipconnect(struct connectdata *conn,
if(data->set.fsockopt) {
/* activate callback for setting socket options */
+ Curl_set_in_callback(data, true);
error = data->set.fsockopt(data->set.sockopt_client,
sockfd,
CURLSOCKTYPE_IPCXN);
+ Curl_set_in_callback(data, false);
if(error == CURL_SOCKOPT_ALREADY_CONNECTED)
isconnected = TRUE;
@@ -1311,8 +1313,12 @@ int Curl_closesocket(struct connectdata *conn,
status */
conn->sock_accepted[SECONDARYSOCKET] = FALSE;
else {
+ int rc;
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);
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
* 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
* here to connect.
*/
+ Curl_set_in_callback(data, true);
*sockfd = data->set.fopensocket(data->set.opensocket_client,
CURLSOCKTYPE_IPCXN,
(struct curl_sockaddr *)addr);
+ Curl_set_in_callback(data, false);
+ }
else
/* opensocket callback not set, so simply create the socket now */
*sockfd = socket(addr->family, addr->socktype, addr->protocol);
diff --git a/lib/easy.c b/lib/easy.c
index 3389d4463..4ebec7cf5 100644
--- a/lib/easy.c
+++ b/lib/easy.c
@@ -61,6 +61,7 @@
#include "strdup.h"
#include "progress.h"
#include "easyif.h"
+#include "multiif.h"
#include "select.h"
#include "sendf.h" /* for failf function prototype */
#include "connect.h" /* for Curl_getconnectinfo */
@@ -761,6 +762,9 @@ static CURLcode easy_perform(struct Curl_easy *data, bool events)
data->multi_easy = multi;
}
+ if(multi->in_callback)
+ return CURLE_RECURSIVE_API_CALL;
+
/* Copy the MAXCONNECTS option to the multi handle */
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.
*
* 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)
{
@@ -1134,6 +1141,9 @@ CURLcode curl_easy_recv(struct Curl_easy *data, void *buffer, size_t buflen,
ssize_t n1;
struct connectdata *c;
+ if(Curl_is_in_callback(data))
+ return CURLE_RECURSIVE_API_CALL;
+
result = easy_connection(data, &sfd, &c);
if(result)
return result;
@@ -1161,6 +1171,9 @@ CURLcode curl_easy_send(struct Curl_easy *data, const void *buffer,
ssize_t n1;
struct connectdata *c = NULL;
+ if(Curl_is_in_callback(data))
+ return CURLE_RECURSIVE_API_CALL;
+
result = easy_connection(data, &sfd, &c);
if(result)
return result;
diff --git a/lib/ftp.c b/lib/ftp.c
index 647a2e325..a119fe1b8 100644
--- a/lib/ftp.c
+++ b/lib/ftp.c
@@ -311,9 +311,11 @@ static CURLcode AcceptServerConnect(struct connectdata *conn)
int error = 0;
/* activate callback for setting socket options */
+ Curl_set_in_callback(data, true);
error = data->set.fsockopt(data->set.sockopt_client,
s,
CURLSOCKTYPE_ACCEPT);
+ Curl_set_in_callback(data, false);
if(error) {
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. */
if(conn->seek_func) {
+ Curl_set_in_callback(data, true);
seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
SEEK_SET);
+ Curl_set_in_callback(data, true);
}
if(seekerr != CURL_SEEKFUNC_OK) {
@@ -3181,7 +3185,9 @@ static CURLcode ftp_done(struct connectdata *conn, CURLcode status,
if(data->state.wildcardmatch) {
if(data->set.chunk_end && ftpc->file) {
+ Curl_set_in_callback(data, true);
data->set.chunk_end(data->wildcard.customptr);
+ Curl_set_in_callback(data, false);
}
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);
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);
+ Curl_set_in_callback(conn->data, false);
switch(userresponse) {
case CURL_CHUNK_BGN_FUNC_SKIP:
infof(conn->data, "Wildcard - \"%s\" skipped by user\n",
@@ -3871,8 +3880,11 @@ static CURLcode wc_statemach(struct connectdata *conn)
} break;
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);
+ Curl_set_in_callback(conn->data, false);
+ }
Curl_llist_remove(&wildcard->filelist, wildcard->filelist.head, NULL);
wildcard->state = (wildcard->filelist.size == 0) ?
CURLWC_CLEAN : CURLWC_DOWNLOADING;
diff --git a/lib/ftplistparser.c b/lib/ftplistparser.c
index 262ac0306..7668ea8c8 100644
--- a/lib/ftplistparser.c
+++ b/lib/ftplistparser.c
@@ -49,6 +49,7 @@
#include "ftplistparser.h"
#include "curl_fnmatch.h"
#include "curl_memory.h"
+#include "multiif.h"
/* The last #include file should be: */
#include "memdebug.h"
@@ -294,6 +295,7 @@ static CURLcode ftp_pl_insert_finfo(struct connectdata *conn,
compare = Curl_fnmatch;
/* filter pattern-corresponding filenames */
+ Curl_set_in_callback(conn->data, true);
if(compare(conn->data->set.fnmatch_data, wc->pattern,
finfo->filename) == 0) {
/* discard symlink which is containing multiple " -> " */
@@ -305,6 +307,7 @@ static CURLcode ftp_pl_insert_finfo(struct connectdata *conn,
else {
add = FALSE;
}
+ Curl_set_in_callback(conn->data, false);
if(add) {
Curl_llist_insert_next(llist, llist->tail, finfo, &infop->list);
diff --git a/lib/http.c b/lib/http.c
index ac8f8ea92..f44b18ae9 100644
--- a/lib/http.c
+++ b/lib/http.c
@@ -2191,8 +2191,10 @@ CURLcode Curl_http(struct connectdata *conn, bool *done)
/* Now, let's read off the proper amount of bytes from the
input. */
if(conn->seek_func) {
+ Curl_set_in_callback(data, true);
seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
SEEK_SET);
+ Curl_set_in_callback(data, false);
}
if(seekerr != CURL_SEEKFUNC_OK) {
diff --git a/lib/http2.c b/lib/http2.c
index d79de4676..d641d94fe 100644
--- a/lib/http2.c
+++ b/lib/http2.c
@@ -464,9 +464,11 @@ static int push_promise(struct Curl_easy *data,
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);
/* free the headers again */
for(i = 0; i<stream->push_headers_used; i++)
diff --git a/lib/multi.c b/lib/multi.c
index 43823cc93..ca3a877eb 100644
--- a/lib/multi.c
+++ b/lib/multi.c
@@ -366,6 +366,9 @@ CURLMcode curl_multi_add_handle(struct Curl_multi *multi,
if(data->multi)
return CURLM_ADDED_ALREADY;
+ if(multi->in_callback)
+ return CURLM_RECURSIVE_API_CALL;
+
/* Initialize timeout list for this handle */
Curl_llist_init(&data->state.timeoutlist, NULL);
@@ -640,6 +643,9 @@ CURLMcode curl_multi_remove_handle(struct Curl_multi *multi,
if(!data->multi)
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;
easy_owns_conn = (data->easy_conn && (data->easy_conn->data == easy)) ?
TRUE : FALSE;
@@ -903,6 +909,9 @@ CURLMcode curl_multi_fdset(struct Curl_multi *multi,
if(!GOOD_MULTI_HANDLE(multi))
return CURLM_BAD_HANDLE;
+ if(multi->in_callback)
+ return CURLM_RECURSIVE_API_CALL;
+
data = multi->easyp;
while(data) {
bitmap = multi_getsock(data, sockbunch, MAX_SOCKSPEREASYHANDLE);
@@ -956,6 +965,9 @@ CURLMcode curl_multi_wait(struct Curl_multi *multi,
if(!GOOD_MULTI_HANDLE(multi))
return CURLM_BAD_HANDLE;
+ if(multi->in_callback)
+ return CURLM_RECURSIVE_API_CALL;
+
/* If the internally desired timeout is actually shorter than requested from
the outside, then use the shorter time! But only if the internal timer
is actually larger than -1! */
@@ -1121,6 +1133,9 @@ CURLMcode Curl_multi_add_perform(struct Curl_multi *multi,
{
CURLMcode rc;
+ if(multi->in_callback)
+ return CURLM_RECURSIVE_API_CALL;
+
rc = curl_multi_add_handle(multi, data);
if(!rc) {
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))
return CURLM_BAD_HANDLE;
+ if(multi->in_callback)
+ return CURLM_RECURSIVE_API_CALL;
+
data = multi->easyp;
while(data) {
CURLMcode result;
@@ -2174,6 +2192,9 @@ CURLMcode curl_multi_cleanup(struct Curl_multi *multi)
struct Curl_easy *nextdata;
if(GOOD_MULTI_HANDLE(multi)) {
+ if(multi->in_callback)
+ return CURLM_RECURSIVE_API_CALL;
+
multi->type = 0; /* not good anymore */
/* 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 */
- 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 */
struct curl_llist_element *e;
@@ -2624,6 +2647,9 @@ CURLMcode curl_multi_setopt(struct Curl_multi *multi,
if(!GOOD_MULTI_HANDLE(multi))
return CURLM_BAD_HANDLE;
+ if(multi->in_callback)
+ return CURLM_RECURSIVE_API_CALL;
+
va_start(param, 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,
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)
update_timer(multi);
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,
int ev_bitmask, int *running_handles)
{
- CURLMcode result = multi_socket(multi, FALSE, s,
- ev_bitmask, running_handles);
+ CURLMcode result;
+ if(multi->in_callback)
+ return CURLM_RECURSIVE_API_CALL;
+ result = multi_socket(multi, FALSE, s, ev_bitmask, running_handles);
if(CURLM_OK >= result)
update_timer(multi);
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 result = multi_socket(multi, TRUE, CURL_SOCKET_BAD, 0,
- running_handles);
+ CURLMcode result;
+ if(multi->in_callback)
+ return CURLM_RECURSIVE_API_CALL;
+ result = multi_socket(multi, TRUE, CURL_SOCKET_BAD, 0, running_handles);
if(CURLM_OK >= result)
update_timer(multi);
return result;
@@ -2760,6 +2793,9 @@ CURLMcode curl_multi_timeout(struct Curl_multi *multi,
if(!GOOD_MULTI_HANDLE(multi))
return CURLM_BAD_HANDLE;
+ if(multi->in_callback)
+ return CURLM_RECURSIVE_API_CALL;
+
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;
+ if(multi->in_callback)
+ return CURLM_RECURSIVE_API_CALL;
+
there = sh_getentry(&multi->sockhash, s);
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
void Curl_multi_dump(struct Curl_multi *multi)
{
diff --git a/lib/multihandle.h b/lib/multihandle.h
index de9a7cf59..1a5017f4a 100644
--- a/lib/multihandle.h
+++ b/lib/multihandle.h
@@ -146,6 +146,7 @@ struct Curl_multi {
void *timer_userp;
struct curltime timer_lastcall; /* the fixed time for the timeout for the
previous callback */
+ bool in_callback; /* true while executing a callback */
};
#endif /* HEADER_CURL_MULTIHANDLE_H */
diff --git a/lib/multiif.h b/lib/multiif.h
index a877571a0..a988bfd4a 100644
--- a/lib/multiif.h
+++ b/lib/multiif.h
@@ -31,6 +31,8 @@ void Curl_expire_clear(struct Curl_easy *data);
void Curl_expire_done(struct Curl_easy *data, expire_id id);
bool Curl_pipeline_wanted(const struct Curl_multi* multi, int bits);
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
socket and connection hashes */
diff --git a/lib/non-ascii.c b/lib/non-ascii.c
index 92b2f8d73..d0e98266b 100644
--- a/lib/non-ascii.c
+++ b/lib/non-ascii.c
@@ -84,7 +84,10 @@ CURLcode Curl_convert_to_network(struct Curl_easy *data,
{
if(data && data->set.convtonetwork) {
/* 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) {
failf(data,
"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) {
/* 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) {
failf(data,
"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) {
/* 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) {
failf(data,
"CURLOPT_CONV_FROM_UTF8_FUNCTION callback returned %d: %s",
diff --git a/lib/progress.c b/lib/progress.c
index cc5e8be79..eef2ca7d4 100644
--- a/lib/progress.c
+++ b/lib/progress.c
@@ -24,6 +24,7 @@
#include "urldata.h"
#include "sendf.h"
+#include "multiif.h"
#include "progress.h"
#include "curl_printf.h"
@@ -461,22 +462,26 @@ int Curl_pgrsUpdate(struct connectdata *conn)
if(data->set.fxferinfo) {
/* There's a callback set, call that */
+ Curl_set_in_callback(data, true);
result = data->set.fxferinfo(data->set.progress_client,
data->progress.size_dl,
data->progress.downloaded,
data->progress.size_ul,
data->progress.uploaded);
+ Curl_set_in_callback(data, false);
if(result)
failf(data, "Callback aborted");
return result;
}
if(data->set.fprogress) {
/* The older deprecated callback is set, call that */
+ Curl_set_in_callback(data, true);
result = data->set.fprogress(data->set.progress_client,
(double)data->progress.size_dl,
(double)data->progress.downloaded,
(double)data->progress.size_ul,
(double)data->progress.uploaded);
+ Curl_set_in_callback(data, false);
if(result)
failf(data, "Callback aborted");
return result;
diff --git a/lib/rtsp.c b/lib/rtsp.c
index 925da2c1a..168e24593 100644
--- a/lib/rtsp.c
+++ b/lib/rtsp.c
@@ -770,7 +770,9 @@ CURLcode rtp_client_write(struct connectdata *conn, char *ptr, size_t len)
user_ptr = data->set.out;
}
+ Curl_set_in_callback(data, true);
wrote = writeit(ptr, 1, len, user_ptr);
+ Curl_set_in_callback(data, false);
if(CURL_WRITEFUNC_PAUSE == wrote) {
failf(data, "Cannot pause RTP");
diff --git a/lib/sendf.c b/lib/sendf.c
index 027f97c47..0fca81555 100644
--- a/lib/sendf.c
+++ b/lib/sendf.c
@@ -37,6 +37,7 @@
#include "connect.h"
#include "vtls/vtls.h"
#include "ssh.h"
+#include "easyif.h"
#include "multiif.h"
#include "non-ascii.h"
#include "strerror.h"
@@ -598,7 +599,10 @@ CURLcode Curl_client_chop_write(struct connectdata *conn,
}
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)
/* 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 */
- 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);
+ Curl_set_in_callback(data, false);
+ }
else {
switch(type) {
case CURLINFO_TEXT:
diff --git a/lib/setopt.c b/lib/setopt.c
index 686e9dbce..69f98a64d 100644
--- a/lib/setopt.c
+++ b/lib/setopt.c
@@ -43,6 +43,7 @@
#include "sendf.h"
#include "http2.h"
#include "setopt.h"
+#include "multiif.h"
/* The last 3 #include files should be in this order */
#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
* easy handle.
+ *
+ * NOTE: This is one of few API functions that are allowed to be called from
+ * within a callback.
*/
#undef curl_easy_setopt
diff --git a/lib/ssh-libssh.c b/lib/ssh-libssh.c
index d821c4a37..9e6667295 100644
--- a/lib/ssh-libssh.c
+++ b/lib/ssh-libssh.c
@@ -383,8 +383,10 @@ static int myssh_is_known(struct connectdata *conn)
}
/* 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 */
keymatch, data->set.ssh_keyfunc_userp);
+ Curl_set_in_callback(data, false);
switch(rc) {
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) {
/* Let's read off the proper amount of bytes from the input. */
if(conn->seek_func) {
+ Curl_set_in_callback(data, true);
seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
SEEK_SET);
+ Curl_set_in_callback(data, false);
}
if(seekerr != CURL_SEEKFUNC_OK) {
diff --git a/lib/ssh.c b/lib/ssh.c
index ae090997b..b7a50a281 100644
--- a/lib/ssh.c
+++ b/lib/ssh.c
@@ -523,9 +523,11 @@ static CURLcode ssh_knownhost(struct connectdata *conn)
keymatch = (enum curl_khmatch)keycheck;
/* Ask the callback how to behave */
+ Curl_set_in_callback(data, true);
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);
}
else
/* no remotekey means failure! */
@@ -1747,8 +1749,10 @@ static CURLcode ssh_statemach_act(struct connectdata *conn, bool *block)
if(data->state.resume_from > 0) {
/* Let's read off the proper amount of bytes from the input. */
if(conn->seek_func) {
+ Curl_set_in_callback(data, true);
seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
SEEK_SET);
+ Curl_set_in_callback(data, false);
}
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 :
curlx_sotouz(data->state.resume_from - passed);
- size_t actuallyread =
- data->state.fread_func(data->state.buffer, 1,
- readthisamountnow, data->state.in);
+ size_t actuallyread;
+ Curl_set_in_callback(data, true);
+ actuallyread = data->state.fread_func(data->state.buffer, 1,
+ readthisamountnow,
+ data->state.in);
+ Curl_set_in_callback(data, false);
passed += actuallyread;
if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
diff --git a/lib/strerror.c b/lib/strerror.c
index 83a96dda1..0295d6c27 100644
--- a/lib/strerror.c
+++ b/lib/strerror.c
@@ -312,6 +312,9 @@ curl_easy_strerror(CURLcode error)
case CURLE_HTTP2_STREAM:
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 */
case CURLE_OBSOLETE20:
case CURLE_OBSOLETE24:
@@ -380,6 +383,9 @@ curl_multi_strerror(CURLMcode error)
case CURLM_ADDED_ALREADY:
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:
break;
}
diff --git a/lib/transfer.c b/lib/transfer.c
index 8f15b1a15..e27792c10 100644
--- a/lib/transfer.c
+++ b/lib/transfer.c
@@ -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
with picky compilers */
+ Curl_set_in_callback(data, true);
nread = (int)data->state.fread_func(data->req.upload_fromhere, 1,
buffersize, data->state.in);
+ Curl_set_in_callback(data, false);
if(nread == CURL_READFUNC_ABORT) {
failf(data, "operation aborted by callback");
@@ -302,7 +304,9 @@ CURLcode Curl_readrewind(struct connectdata *conn)
if(data->set.seek_func) {
int err;
+ Curl_set_in_callback(data, true);
err = (data->set.seek_func)(data->set.seek_client, 0, SEEK_SET);
+ Curl_set_in_callback(data, false);
if(err) {
failf(data, "seek callback returned error %d", (int)err);
return CURLE_SEND_FAIL_REWIND;
@@ -311,8 +315,10 @@ CURLcode Curl_readrewind(struct connectdata *conn)
else if(data->set.ioctl_func) {
curlioerr err;
+ Curl_set_in_callback(data, true);
err = (data->set.ioctl_func)(data, CURLIOCMD_RESTARTREAD,
data->set.ioctl_client);
+ Curl_set_in_callback(data, false);
infof(data, "the ioctl callback returned %d\n", (int)err);
if(err) {
diff --git a/tests/data/Makefile.inc b/tests/data/Makefile.inc
index 34fdbfffb..335570ae6 100644
--- a/tests/data/Makefile.inc
+++ b/tests/data/Makefile.inc
@@ -171,7 +171,7 @@ test1520 test1521 \
test1525 test1526 test1527 test1528 test1529 test1530 test1531 test1532 \
test1533 test1534 test1535 test1536 test1537 test1538 \
test1540 \
-test1550 test1551 test1552 test1553 test1554 \
+test1550 test1551 test1552 test1553 test1554 test1555 \
test1600 test1601 test1602 test1603 test1604 test1605 test1606 \
\
test1700 test1701 test1702 \
diff --git a/tests/data/test1538 b/tests/data/test1538
index 25b39a6f6..b084dac6d 100644
--- a/tests/data/test1538
+++ b/tests/data/test1538
@@ -125,7 +125,8 @@ e89: The max connection limit is reached
e90: SSL public key does not match pinned public key
e91: SSL server certificate status verification FAILED
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
m0: No error
m1: Invalid multi handle
@@ -135,7 +136,8 @@ m4: Internal error
m5: Invalid socket argument
m6: Unknown option
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
s1: Unknown share option
s2: Share currently in use
diff --git a/tests/data/test1555 b/tests/data/test1555
new file mode 100644
index 000000000..19512c5f5
--- /dev/null
+++ b/tests/data/test1555
@@ -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>
diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc
index d8a55e21d..c23dedd5e 100644
--- a/tests/libtest/Makefile.inc
+++ b/tests/libtest/Makefile.inc
@@ -27,7 +27,7 @@ noinst_PROGRAMS = chkhostname libauthretry libntlmconnect \
lib1525 lib1526 lib1527 lib1528 lib1529 lib1530 lib1531 lib1532 lib1533 \
lib1534 lib1535 lib1536 lib1537 lib1538 \
lib1540 \
- lib1550 lib1551 lib1552 lib1553 lib1554 \
+ lib1550 lib1551 lib1552 lib1553 lib1554 lib1555 \
lib1900 \
lib2033
@@ -477,6 +477,10 @@ lib1553_CPPFLAGS = $(AM_CPPFLAGS)
lib1554_SOURCES = lib1554.c $(SUPPORTFILES)
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_LDADD = $(TESTUTIL_LIBS)
lib1900_CPPFLAGS = $(AM_CPPFLAGS)
diff --git a/tests/libtest/lib1555.c b/tests/libtest/lib1555.c
new file mode 100644
index 000000000..e4f2255ac
--- /dev/null
+++ b/tests/libtest/lib1555.c
@@ -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;
+}