diff --git a/docs/libcurl/libcurl-errors.3 b/docs/libcurl/libcurl-errors.3 index 1b6e34f5138094..2c866b0d2f7cb4 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 306b7a845fb37f..ac3dadee38e4a2 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 69283eb0836432..7cab0a16c756e2 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 911c91dd1a6d14..bd106ae850d7d5 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 3edb71eb722398..d56cf2f16e9ff4 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 3389d446326c8b..4ebec7cf590180 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 647a2e3258fe4c..a119fe1b89835d 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 262ac03062b29f..7668ea8c8cbe7c 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 ac8f8ea926d57b..f44b18ae93739f 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 d79de4676d5ab7..d641d94fea552f 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; ipush_headers_used; i++) diff --git a/lib/multi.c b/lib/multi.c index 43823cc9311b1b..ca3a877eb47869 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 de9a7cf59c583e..1a5017f4a02649 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 a877571a00a85f..a988bfd4a2b08a 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 92b2f8d73206ae..d0e98266bc7002 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 cc5e8be794850a..eef2ca7d4fb4ca 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 925da2c1a9bf3c..168e24593ef117 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 027f97c47a7e5d..0fca81555b3dc3 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 686e9dbcee9c36..69f98a64d481a2 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 d821c4a37ab371..9e6667295d75a4 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 ae090997b0e5dd..b7a50a281d6c3a 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 83a96dda195ef3..0295d6c27d8153 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 8f15b1a1527bcf..e27792c1036353 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 34fdbfffbbc3ad..335570ae6f6c2a 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 25b39a6f6d54c4..b084dac6d6380a 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 00000000000000..19512c5f58d763 --- /dev/null +++ b/tests/data/test1555 @@ -0,0 +1,50 @@ + + + +RECURSIVE_API_CALL + + + +# Server-side + + +HTTP/1.1 204 PARTIAL +X-Comment: partial response to keep the client waiting + + +wait 10 + + + +# Client-side + + +http + + +lib1555 + + +verify api is protected against calls from callbacks + + +# this server/host won't be used for real + +http://%HOSTIP:%HTTPPORT/1555 + + + +# Verify data after the test has been "shot" + + + +# 42 == CURLE_ABORTED_BY_CALLBACK + +42 + + +curl_easy_recv returned 93 +curl_easy_send returned 93 + + + diff --git a/tests/libtest/Makefile.inc b/tests/libtest/Makefile.inc index d8a55e21dd5a5d..c23dedd5eef07a 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 00000000000000..e4f2255ac17172 --- /dev/null +++ b/tests/libtest/lib1555.c @@ -0,0 +1,77 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2015, 2017, Daniel Stenberg, , 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; +}