lib: reject internal handles in the multi/easy public API
No binary (build failed)
—
(log yok)
| @@ -40,6 +40,10 @@ handles and transfers remain unaffected. | |
| It is fine to remove a handle at any time during a transfer, but not from | |
| within any libcurl callback function. | |
| + | An internal handle, such as one passed to a CURLMOPT_NOTIFYFUNCTION(3) |
| + | callback, cannot be removed and makes this function return |
| + | CURLM_BAD_EASY_HANDLE. |
| + | |
| Removing an easy handle from the multi handle before the corresponding | |
| transfer is complete might cause libcurl to close the connection - if the | |
| state of it and the internal protocol handler deem it necessary. Otherwise | |
| @@ -51,6 +51,11 @@ and curl_multi_cleanup(3) it may call all other methods on the | |
| multi and easy handles. This includes adding and removing easy | |
| handles to/from the multi handle. | |
| + | Internal handles are owned by libcurl and the public API rejects them: the |
| + | easy interface functions fail with CURLE_BAD_FUNCTION_ARGUMENT, and |
| + | curl_multi_add_handle(3) and curl_multi_remove_handle(3) return |
| + | CURLM_BAD_EASY_HANDLE. |
| + | |
| This callback may get invoked at any time when interacting with libcurl. | |
| This may even happen after all transfers are done and *may also* | |
| happen *during* a call to curl_multi_cleanup(3) when cached connections | |
| @@ -196,7 +196,7 @@ bool Curl_eapi_enter(struct Curl_eapi_guard *guard, | |
| guard->depth = 0; | |
| /* Verify that we got an easy handle we can work with. */ | |
| - | if(!GOOD_EASY_HANDLE(data)) { |
| + | if(!GOOD_EASY_HANDLE(data) || data->state.internal) { |
| result = CURLE_BAD_FUNCTION_ARGUMENT; | |
| goto out; | |
| } | |
| @@ -581,7 +581,7 @@ CURLMcode curl_multi_add_handle(CURLM *m, CURL *curl) | |
| if(CURL_MAPI_ENTER(&guard, m, multi_add_handle, &mresult)) { | |
| struct Curl_easy *data = curl; | |
| /* Verify that we got a somewhat good easy handle too */ | |
| - | if(!GOOD_EASY_HANDLE(data)) |
| + | if(!GOOD_EASY_HANDLE(data) || data->state.internal) |
| mresult = CURLM_BAD_EASY_HANDLE; | |
| else | |
| mresult = Curl_multi_add_handle(m, data); | |
| @@ -901,7 +901,7 @@ CURLMcode curl_multi_remove_handle(CURLM *m, CURL *curl) | |
| if(CURL_MAPI_ENTER(&guard, m, multi_remove_handle, &mresult)) { | |
| struct Curl_easy *data = curl; | |
| - | if(!GOOD_EASY_HANDLE(data)) |
| + | if(!GOOD_EASY_HANDLE(data) || data->state.internal) |
| mresult = CURLM_BAD_EASY_HANDLE; | |
| else | |
| mresult = Curl_multi_remove_handle(m, data); | |
| @@ -2057,6 +2057,7 @@ TESTCASES = \ | |
| test3104 \ | |
| test3105 \ | |
| test3106 \ | |
| + | test3107 \ |
| test3109 \ | |
| test3200 \ | |
| test3201 \ | |
| @@ -0,0 +1,47 @@ | |
| + | <?xml version="1.0" encoding="US-ASCII"?> |
| + | <testcase> |
| + | <info> |
| + | <keywords> |
| + | HTTP |
| + | HTTP GET |
| + | multi |
| + | curl_multi_remove_handle |
| + | </keywords> |
| + | </info> |
| + | |
| + | # Server-side |
| + | <reply> |
| + | <data nocheck="yes"> |
| + | HTTP/1.1 200 OK |
| + | Content-Length: 6 |
| + | |
| + | hello |
| + | </data> |
| + | </reply> |
| + | |
| + | # Client-side |
| + | <client> |
| + | <server> |
| + | http |
| + | </server> |
| + | <tool> |
| + | lib%TESTNUMBER |
| + | </tool> |
| + | <name> |
| + | reject internal easy handle passed to a notify callback |
| + | </name> |
| + | <command> |
| + | http://%HOSTIP:%HTTPPORT/%TESTNUMBER |
| + | </command> |
| + | </client> |
| + | |
| + | # Verify data after the test has been "shot" |
| + | <verify> |
| + | <protocol crlf="headers"> |
| + | GET /%TESTNUMBER HTTP/1.1 |
| + | Host: %HOSTIP:%HTTPPORT |
| + | Accept: */* |
| + | |
| + | </protocol> |
| + | </verify> |
| + | </testcase> |
| @@ -329,6 +329,7 @@ TESTS_C = \ | |
| lib3103.c \ | |
| lib3104.c \ | |
| lib3105.c \ | |
| + | lib3107.c \ |
| lib3207.c \ | |
| lib3208.c \ | |
| lib3231.c \ | |
| @@ -0,0 +1,105 @@ | |
| + | /*************************************************************************** |
| + | * _ _ ____ _ |
| + | * Project ___| | | | _ \| | |
| + | * / __| | | | |_) | | |
| + | * | (__| |_| | _ <| |___ |
| + | * \___|\___/|_| \_\_____| |
| + | * |
| + | * Copyright (C) Daniel Stenberg, <[email protected]>, et al. |
| + | * |
| + | * This software is licensed as described in the file COPYING, which |
| + | * you should have received as part of this distribution. The terms |
| + | * are also available at https://curl.se/docs/copyright.html. |
| + | * |
| + | * You may opt to use, copy, modify, merge, publish, distribute and/or sell |
| + | * copies of the Software, and permit persons to whom the Software is |
| + | * furnished to do so, under the terms of the COPYING file. |
| + | * |
| + | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY |
| + | * KIND, either express or implied. |
| + | * |
| + | * SPDX-License-Identifier: curl |
| + | * |
| + | ***************************************************************************/ |
| + | #include "first.h" |
| + | |
| + | static int t3107_notified; |
| + | static CURLMcode t3107_mres; |
| + | static CURLcode t3107_eres; |
| + | |
| + | static void t3107_notify_cb(CURLM *multi, unsigned int notification, |
| + | CURL *easy, void *user_data) |
| + | { |
| + | (void)user_data; |
| + | if(notification != CURLMNOTIFY_INFO_READ) |
| + | return; |
| + | t3107_notified++; |
| + | t3107_mres = curl_multi_remove_handle(multi, easy); |
| + | t3107_eres = curl_easy_setopt(easy, CURLOPT_VERBOSE, 1L); |
| + | curl_easy_cleanup(easy); |
| + | } |
| + | |
| + | static CURLcode test_lib3107(const char *URL) |
| + | { |
| + | CURL *curl = NULL; |
| + | CURLM *multi = NULL; |
| + | CURLcode result = CURLE_OK; |
| + | CURLMsg *msg; |
| + | int running = 1; |
| + | int queued; |
| + | |
| + | global_init(CURL_GLOBAL_ALL); |
| + | |
| + | multi_init(multi); |
| + | multi_setopt(multi, CURLMOPT_NOTIFYFUNCTION, t3107_notify_cb); |
| + | |
| + | if(curl_multi_notify_enable(multi, CURLMNOTIFY_INFO_READ) != CURLM_OK) { |
| + | curl_mfprintf(stderr, "curl_multi_notify_enable() failed\n"); |
| + | result = TEST_ERR_MAJOR_BAD; |
| + | goto test_cleanup; |
| + | } |
| + | |
| + | easy_init(curl); |
| + | easy_setopt(curl, CURLOPT_URL, URL); |
| + | |
| + | multi_add_handle(multi, curl); |
| + | |
| + | while(running) { |
| + | CURLMcode mres = curl_multi_perform(multi, &running); |
| + | if(!mres && running) |
| + | mres = curl_multi_poll(multi, NULL, 0, 1000, NULL); |
| + | if(mres) { |
| + | curl_mfprintf(stderr, "curl_multi failed, with code %d (%s)\n", |
| + | (int)mres, curl_multi_strerror(mres)); |
| + | result = TEST_ERR_MULTI; |
| + | goto test_cleanup; |
| + | } |
| + | } |
| + | |
| + | do { |
| + | msg = curl_multi_info_read(multi, &queued); |
| + | } while(msg); |
| + | |
| + | if(!t3107_notified) { |
| + | curl_mfprintf(stderr, "no CURLMNOTIFY_INFO_READ notification\n"); |
| + | result = TEST_ERR_MAJOR_BAD; |
| + | } |
| + | else if(t3107_mres != CURLM_BAD_EASY_HANDLE) { |
| + | curl_mfprintf(stderr, "curl_multi_remove_handle() returned %d\n", |
| + | (int)t3107_mres); |
| + | result = TEST_ERR_MAJOR_BAD; |
| + | } |
| + | else if(t3107_eres != CURLE_BAD_FUNCTION_ARGUMENT) { |
| + | curl_mfprintf(stderr, "curl_easy_setopt() returned %d\n", |
| + | (int)t3107_eres); |
| + | result = TEST_ERR_MAJOR_BAD; |
| + | } |
| + | |
| + | test_cleanup: |
| + | curl_multi_remove_handle(multi, curl); |
| + | curl_multi_cleanup(multi); |
| + | curl_easy_cleanup(curl); |
| + | curl_global_cleanup(); |
| + | |
| + | return result; |
| + | } |