]> granicus.if.org Git - curl/commitdiff
TODO fixed: Detect when called from within callbacks
authorBjörn Stenberg <bjorn@haxx.se>
Sat, 10 Feb 2018 14:13:15 +0000 (15:13 +0100)
committerDaniel Stenberg <daniel@haxx.se>
Thu, 15 Feb 2018 08:36:03 +0000 (09:36 +0100)
Closes #2302

28 files changed:
docs/TODO
docs/libcurl/libcurl-errors.3
docs/libcurl/symbols-in-versions
include/curl/curl.h
include/curl/multi.h
lib/connect.c
lib/easy.c
lib/ftp.c
lib/ftplistparser.c
lib/http.c
lib/http2.c
lib/multi.c
lib/multihandle.h
lib/multiif.h
lib/non-ascii.c
lib/progress.c
lib/rtsp.c
lib/sendf.c
lib/setopt.c
lib/ssh-libssh.c
lib/ssh.c
lib/strerror.c
lib/transfer.c
tests/data/Makefile.inc
tests/data/test1538
tests/data/test1555 [new file with mode: 0644]
tests/libtest/Makefile.inc
tests/libtest/lib1555.c [new file with mode: 0644]

index ecea1eeb1eb0671c8a2da41b243f3bf2cbb7eefb..c06a9ceeafd742ab2313464d1033163c27c8f284 100644 (file)
--- 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
  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
index 1b6e34f51380948ab133b22156a3b3b4be985a6f..2c866b0d2f7cb4b7a71801d584c276c34e845926 100644 (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.
 .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.
index 306b7a845fb37f8864bd954cb43766f37e14fcc9..ac3dadee38e4a21003f1fb0a4f4945ad47138e1b 100644 (file)
@@ -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
index 69283eb0836432d04161e1ef6f5a7c5db5a27e2f..7cab0a16c756e21bb0b24c89dc09321c52a6242e 100644 (file)
@@ -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;
 
index 911c91dd1a6d1447f7aa2c4ece7d7a9ebcc17d0b..bd106ae850d7d5d22bbf9edfe44a8fc9e0fe6bc3 100644 (file)
@@ -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;
 
index 3edb71eb722398055d5b68bc2f7f054aeb54b852..d56cf2f16e9ff4ee4bbf87a32d296841b3398e0a 100644 (file)
@@ -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);
index 3389d446326c8be0f5dba5c3c0b6c9a07ceea6fa..4ebec7cf5901809a35b22e52a77afeadffddf48b 100644 (file)
@@ -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;
index 647a2e3258fe4c8ddc383b93d685fb046fc60502..a119fe1b89835d2bc42424b8f5d47f055f94ebb7 100644 (file)
--- 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;
index 262ac03062b29fe0f35eebd17852b484128d68d8..7668ea8c8cbe7ccb63a8dd27c7893b123e378267 100644 (file)
@@ -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);
index ac8f8ea926d57b0525a8e1775000c30d392aeaa6..f44b18ae93739f34a4268db5b27cde2d901d1600 100644 (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
          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) {
index d79de4676d5ab739ecac82961de62c8e051a257c..d641d94fea552f559da3b2e20f3c400a9b4c2bd8 100644 (file)
@@ -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++)
index 43823cc9311b1b1bb09df9e002145bfa1a60215b..ca3a877eb4786990fe8e70b13978a9447c576856 100644 (file)
@@ -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)
 {
index de9a7cf59c583e860301f4420fa39b0e3959da7b..1a5017f4a02649c14c00d2f7ac26bbb24f9df131 100644 (file)
@@ -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 */
index a877571a00a85f2491e761c8cf80a6a2321bc805..a988bfd4a2b08aead067eb9c5a329c62c97e26b7 100644 (file)
@@ -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 */
index 92b2f8d73206ae23b123b0aad559eccd174aff48..d0e98266bc70023c1dfbf5416f8ce515bdcc78a1 100644 (file)
@@ -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",
index cc5e8be794850ac518de79fc6d8338bbe616fbfe..eef2ca7d4fb4cac04f66ffafa17ebf87cc7e3f50 100644 (file)
@@ -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;
index 925da2c1a9bf3cdb02ab585937a676b3d3c0442c..168e24593ef117250555dca231c2698c2470a95d 100644 (file)
@@ -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");
index 027f97c47a7e5d551b3c3d6dc6e199792d25c56b..0fca81555b3dc353c0bb5d719992ba2df45ba70d 100644 (file)
@@ -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:
index 686e9dbcee9c3631f3e12fec3d4ebbeabff6f3aa..69f98a64d481a2fd47d0765450e49faae41c856f 100644 (file)
@@ -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
index d821c4a37ab371c5b3c852d6d45b8e3edf358585..9e6667295d75a4997437a602591ffd2f801b8d98 100644 (file)
@@ -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) {
index ae090997b0e5dd073f9d3ae47b4c45c204958b2e..b7a50a281d6c3ad5c27733b8a0b7da80d5e9e97a 100644 (file)
--- 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)) {
index 83a96dda195ef3ecb520bea6eade20e8f1eb2828..0295d6c27d8153ed6ed31a67465e0e62849781eb 100644 (file)
@@ -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;
   }
index 8f15b1a1527bcf67a2dfb436319b8baa586da856..e27792c103635322e8b2aee7af4804ee867e6562 100644 (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
      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) {
index 34fdbfffbbc3ad8f3ed49afe1fcdef4601c39f31..335570ae6f6c2a2ebb9b31507aa49c28b536dcbb 100644 (file)
@@ -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 \
index 25b39a6f6d54c4642b51e136cc43f4b74b115ced..b084dac6d6380a39aa74a3dca3d0236cf8b48baa 100644 (file)
@@ -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 (file)
index 0000000..19512c5
--- /dev/null
@@ -0,0 +1,50 @@
+<testcase>
+<info>
+<keywords>
+RECURSIVE_API_CALL
+</keywords>
+</info>
+
+# Server-side
+<reply>
+<data nocheck="yes">
+HTTP/1.1 204 PARTIAL\r
+X-Comment: partial response to keep the client waiting\r
+</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>
index d8a55e21dd5a5d77d5b57925f2a85f760bf95a87..c23dedd5eef07ac8f858e7d0362203243309f6cf 100644 (file)
@@ -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 (file)
index 0000000..e4f2255
--- /dev/null
@@ -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;
+}