113498266Sopenharmony_ci/*************************************************************************** 213498266Sopenharmony_ci * _ _ ____ _ 313498266Sopenharmony_ci * Project ___| | | | _ \| | 413498266Sopenharmony_ci * / __| | | | |_) | | 513498266Sopenharmony_ci * | (__| |_| | _ <| |___ 613498266Sopenharmony_ci * \___|\___/|_| \_\_____| 713498266Sopenharmony_ci * 813498266Sopenharmony_ci * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 913498266Sopenharmony_ci * 1013498266Sopenharmony_ci * This software is licensed as described in the file COPYING, which 1113498266Sopenharmony_ci * you should have received as part of this distribution. The terms 1213498266Sopenharmony_ci * are also available at https://curl.se/docs/copyright.html. 1313498266Sopenharmony_ci * 1413498266Sopenharmony_ci * You may opt to use, copy, modify, merge, publish, distribute and/or sell 1513498266Sopenharmony_ci * copies of the Software, and permit persons to whom the Software is 1613498266Sopenharmony_ci * furnished to do so, under the terms of the COPYING file. 1713498266Sopenharmony_ci * 1813498266Sopenharmony_ci * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 1913498266Sopenharmony_ci * KIND, either express or implied. 2013498266Sopenharmony_ci * 2113498266Sopenharmony_ci * SPDX-License-Identifier: curl 2213498266Sopenharmony_ci * 2313498266Sopenharmony_ci ***************************************************************************/ 2413498266Sopenharmony_ci 2513498266Sopenharmony_ci/* 2613498266Sopenharmony_ci * The purpose of this test is to make sure that if CURLMOPT_SOCKETFUNCTION or 2713498266Sopenharmony_ci * CURLMOPT_TIMERFUNCTION returns error, the associated transfer should be 2813498266Sopenharmony_ci * aborted correctly. 2913498266Sopenharmony_ci */ 3013498266Sopenharmony_ci 3113498266Sopenharmony_ci#include "test.h" 3213498266Sopenharmony_ci 3313498266Sopenharmony_ci#include <fcntl.h> 3413498266Sopenharmony_ci 3513498266Sopenharmony_ci#include "testutil.h" 3613498266Sopenharmony_ci#include "warnless.h" 3713498266Sopenharmony_ci#include "memdebug.h" 3813498266Sopenharmony_ci 3913498266Sopenharmony_ci#define TEST_HANG_TIMEOUT 60 * 1000 4013498266Sopenharmony_ci 4113498266Sopenharmony_cistruct Sockets 4213498266Sopenharmony_ci{ 4313498266Sopenharmony_ci curl_socket_t *sockets; 4413498266Sopenharmony_ci int count; /* number of sockets actually stored in array */ 4513498266Sopenharmony_ci int max_count; /* max number of sockets that fit in allocated array */ 4613498266Sopenharmony_ci}; 4713498266Sopenharmony_ci 4813498266Sopenharmony_cistruct ReadWriteSockets 4913498266Sopenharmony_ci{ 5013498266Sopenharmony_ci struct Sockets read, write; 5113498266Sopenharmony_ci}; 5213498266Sopenharmony_ci 5313498266Sopenharmony_ci/** 5413498266Sopenharmony_ci * Remove a file descriptor from a sockets array. 5513498266Sopenharmony_ci */ 5613498266Sopenharmony_cistatic void removeFd(struct Sockets *sockets, curl_socket_t fd, int mention) 5713498266Sopenharmony_ci{ 5813498266Sopenharmony_ci int i; 5913498266Sopenharmony_ci 6013498266Sopenharmony_ci if(mention) 6113498266Sopenharmony_ci fprintf(stderr, "Remove socket fd %d\n", (int) fd); 6213498266Sopenharmony_ci 6313498266Sopenharmony_ci for(i = 0; i < sockets->count; ++i) { 6413498266Sopenharmony_ci if(sockets->sockets[i] == fd) { 6513498266Sopenharmony_ci if(i < sockets->count - 1) 6613498266Sopenharmony_ci memmove(&sockets->sockets[i], &sockets->sockets[i + 1], 6713498266Sopenharmony_ci sizeof(curl_socket_t) * (sockets->count - (i + 1))); 6813498266Sopenharmony_ci --sockets->count; 6913498266Sopenharmony_ci } 7013498266Sopenharmony_ci } 7113498266Sopenharmony_ci} 7213498266Sopenharmony_ci 7313498266Sopenharmony_ci/** 7413498266Sopenharmony_ci * Add a file descriptor to a sockets array. 7513498266Sopenharmony_ci * Return 0 on success, 1 on error. 7613498266Sopenharmony_ci */ 7713498266Sopenharmony_cistatic int addFd(struct Sockets *sockets, curl_socket_t fd, const char *what) 7813498266Sopenharmony_ci{ 7913498266Sopenharmony_ci /** 8013498266Sopenharmony_ci * To ensure we only have each file descriptor once, we remove it then add 8113498266Sopenharmony_ci * it again. 8213498266Sopenharmony_ci */ 8313498266Sopenharmony_ci fprintf(stderr, "Add socket fd %d for %s\n", (int) fd, what); 8413498266Sopenharmony_ci removeFd(sockets, fd, 0); 8513498266Sopenharmony_ci /* 8613498266Sopenharmony_ci * Allocate array storage when required. 8713498266Sopenharmony_ci */ 8813498266Sopenharmony_ci if(!sockets->sockets) { 8913498266Sopenharmony_ci sockets->sockets = malloc(sizeof(curl_socket_t) * 20U); 9013498266Sopenharmony_ci if(!sockets->sockets) 9113498266Sopenharmony_ci return 1; 9213498266Sopenharmony_ci sockets->max_count = 20; 9313498266Sopenharmony_ci } 9413498266Sopenharmony_ci else if(sockets->count + 1 > sockets->max_count) { 9513498266Sopenharmony_ci curl_socket_t *ptr = realloc(sockets->sockets, sizeof(curl_socket_t) * 9613498266Sopenharmony_ci (sockets->max_count + 20)); 9713498266Sopenharmony_ci if(!ptr) 9813498266Sopenharmony_ci /* cleanup in test_cleanup */ 9913498266Sopenharmony_ci return 1; 10013498266Sopenharmony_ci sockets->sockets = ptr; 10113498266Sopenharmony_ci sockets->max_count += 20; 10213498266Sopenharmony_ci } 10313498266Sopenharmony_ci /* 10413498266Sopenharmony_ci * Add file descriptor to array. 10513498266Sopenharmony_ci */ 10613498266Sopenharmony_ci sockets->sockets[sockets->count] = fd; 10713498266Sopenharmony_ci ++sockets->count; 10813498266Sopenharmony_ci return 0; 10913498266Sopenharmony_ci} 11013498266Sopenharmony_ci 11113498266Sopenharmony_cistatic int max_socket_calls; 11213498266Sopenharmony_cistatic int socket_calls = 0; 11313498266Sopenharmony_ci 11413498266Sopenharmony_ci/** 11513498266Sopenharmony_ci * Callback invoked by curl to poll reading / writing of a socket. 11613498266Sopenharmony_ci */ 11713498266Sopenharmony_cistatic int curlSocketCallback(CURL *easy, curl_socket_t s, int action, 11813498266Sopenharmony_ci void *userp, void *socketp) 11913498266Sopenharmony_ci{ 12013498266Sopenharmony_ci struct ReadWriteSockets *sockets = userp; 12113498266Sopenharmony_ci 12213498266Sopenharmony_ci (void)easy; /* unused */ 12313498266Sopenharmony_ci (void)socketp; /* unused */ 12413498266Sopenharmony_ci 12513498266Sopenharmony_ci fprintf(stderr, "CURLMOPT_SOCKETFUNCTION called: %u\n", socket_calls++); 12613498266Sopenharmony_ci if(socket_calls == max_socket_calls) { 12713498266Sopenharmony_ci fprintf(stderr, "curlSocketCallback returns error\n"); 12813498266Sopenharmony_ci return -1; 12913498266Sopenharmony_ci } 13013498266Sopenharmony_ci 13113498266Sopenharmony_ci if(action == CURL_POLL_IN || action == CURL_POLL_INOUT) 13213498266Sopenharmony_ci if(addFd(&sockets->read, s, "read")) 13313498266Sopenharmony_ci return -1; /* bail out */ 13413498266Sopenharmony_ci 13513498266Sopenharmony_ci if(action == CURL_POLL_OUT || action == CURL_POLL_INOUT) 13613498266Sopenharmony_ci if(addFd(&sockets->write, s, "write")) 13713498266Sopenharmony_ci return -1; 13813498266Sopenharmony_ci 13913498266Sopenharmony_ci if(action == CURL_POLL_REMOVE) { 14013498266Sopenharmony_ci removeFd(&sockets->read, s, 1); 14113498266Sopenharmony_ci removeFd(&sockets->write, s, 0); 14213498266Sopenharmony_ci } 14313498266Sopenharmony_ci 14413498266Sopenharmony_ci return 0; 14513498266Sopenharmony_ci} 14613498266Sopenharmony_ci 14713498266Sopenharmony_cistatic int max_timer_calls; 14813498266Sopenharmony_cistatic int timer_calls = 0; 14913498266Sopenharmony_ci 15013498266Sopenharmony_ci/** 15113498266Sopenharmony_ci * Callback invoked by curl to set a timeout. 15213498266Sopenharmony_ci */ 15313498266Sopenharmony_cistatic int curlTimerCallback(CURLM *multi, long timeout_ms, void *userp) 15413498266Sopenharmony_ci{ 15513498266Sopenharmony_ci struct timeval *timeout = userp; 15613498266Sopenharmony_ci 15713498266Sopenharmony_ci (void)multi; /* unused */ 15813498266Sopenharmony_ci fprintf(stderr, "CURLMOPT_TIMERFUNCTION called: %u\n", timer_calls++); 15913498266Sopenharmony_ci if(timer_calls == max_timer_calls) { 16013498266Sopenharmony_ci fprintf(stderr, "curlTimerCallback returns error\n"); 16113498266Sopenharmony_ci return -1; 16213498266Sopenharmony_ci } 16313498266Sopenharmony_ci if(timeout_ms != -1) { 16413498266Sopenharmony_ci *timeout = tutil_tvnow(); 16513498266Sopenharmony_ci timeout->tv_usec += (int)timeout_ms * 1000; 16613498266Sopenharmony_ci } 16713498266Sopenharmony_ci else { 16813498266Sopenharmony_ci timeout->tv_sec = -1; 16913498266Sopenharmony_ci } 17013498266Sopenharmony_ci return 0; 17113498266Sopenharmony_ci} 17213498266Sopenharmony_ci 17313498266Sopenharmony_ci/** 17413498266Sopenharmony_ci * Check for curl completion. 17513498266Sopenharmony_ci */ 17613498266Sopenharmony_cistatic int checkForCompletion(CURLM *curl, int *success) 17713498266Sopenharmony_ci{ 17813498266Sopenharmony_ci int result = 0; 17913498266Sopenharmony_ci *success = 0; 18013498266Sopenharmony_ci while(1) { 18113498266Sopenharmony_ci int numMessages; 18213498266Sopenharmony_ci CURLMsg *message = curl_multi_info_read(curl, &numMessages); 18313498266Sopenharmony_ci if(!message) 18413498266Sopenharmony_ci break; 18513498266Sopenharmony_ci if(message->msg == CURLMSG_DONE) { 18613498266Sopenharmony_ci result = 1; 18713498266Sopenharmony_ci if(message->data.result == CURLE_OK) 18813498266Sopenharmony_ci *success = 1; 18913498266Sopenharmony_ci else 19013498266Sopenharmony_ci *success = 0; 19113498266Sopenharmony_ci } 19213498266Sopenharmony_ci else { 19313498266Sopenharmony_ci fprintf(stderr, "Got an unexpected message from curl: %i\n", 19413498266Sopenharmony_ci (int)message->msg); 19513498266Sopenharmony_ci result = 1; 19613498266Sopenharmony_ci *success = 0; 19713498266Sopenharmony_ci } 19813498266Sopenharmony_ci } 19913498266Sopenharmony_ci return result; 20013498266Sopenharmony_ci} 20113498266Sopenharmony_ci 20213498266Sopenharmony_cistatic int getMicroSecondTimeout(struct timeval *timeout) 20313498266Sopenharmony_ci{ 20413498266Sopenharmony_ci struct timeval now; 20513498266Sopenharmony_ci ssize_t result; 20613498266Sopenharmony_ci now = tutil_tvnow(); 20713498266Sopenharmony_ci result = (ssize_t)((timeout->tv_sec - now.tv_sec) * 1000000 + 20813498266Sopenharmony_ci timeout->tv_usec - now.tv_usec); 20913498266Sopenharmony_ci if(result < 0) 21013498266Sopenharmony_ci result = 0; 21113498266Sopenharmony_ci 21213498266Sopenharmony_ci return curlx_sztosi(result); 21313498266Sopenharmony_ci} 21413498266Sopenharmony_ci 21513498266Sopenharmony_ci/** 21613498266Sopenharmony_ci * Update a fd_set with all of the sockets in use. 21713498266Sopenharmony_ci */ 21813498266Sopenharmony_cistatic void updateFdSet(struct Sockets *sockets, fd_set* fdset, 21913498266Sopenharmony_ci curl_socket_t *maxFd) 22013498266Sopenharmony_ci{ 22113498266Sopenharmony_ci int i; 22213498266Sopenharmony_ci for(i = 0; i < sockets->count; ++i) { 22313498266Sopenharmony_ci FD_SET(sockets->sockets[i], fdset); 22413498266Sopenharmony_ci if(*maxFd < sockets->sockets[i] + 1) { 22513498266Sopenharmony_ci *maxFd = sockets->sockets[i] + 1; 22613498266Sopenharmony_ci } 22713498266Sopenharmony_ci } 22813498266Sopenharmony_ci} 22913498266Sopenharmony_ci 23013498266Sopenharmony_cistatic int socket_action(CURLM *curl, curl_socket_t s, int evBitmask, 23113498266Sopenharmony_ci const char *info) 23213498266Sopenharmony_ci{ 23313498266Sopenharmony_ci int numhandles = 0; 23413498266Sopenharmony_ci CURLMcode result = curl_multi_socket_action(curl, s, evBitmask, &numhandles); 23513498266Sopenharmony_ci if(result != CURLM_OK) { 23613498266Sopenharmony_ci fprintf(stderr, "Curl error on %s: %i (%s)\n", 23713498266Sopenharmony_ci info, result, curl_multi_strerror(result)); 23813498266Sopenharmony_ci } 23913498266Sopenharmony_ci return (int)result; 24013498266Sopenharmony_ci} 24113498266Sopenharmony_ci 24213498266Sopenharmony_ci/** 24313498266Sopenharmony_ci * Invoke curl when a file descriptor is set. 24413498266Sopenharmony_ci */ 24513498266Sopenharmony_cistatic int checkFdSet(CURLM *curl, 24613498266Sopenharmony_ci struct Sockets *sockets, fd_set *fdset, 24713498266Sopenharmony_ci int evBitmask, const char *name) 24813498266Sopenharmony_ci{ 24913498266Sopenharmony_ci int i; 25013498266Sopenharmony_ci int result = 0; 25113498266Sopenharmony_ci for(i = 0; i < sockets->count; ++i) { 25213498266Sopenharmony_ci if(FD_ISSET(sockets->sockets[i], fdset)) { 25313498266Sopenharmony_ci result = socket_action(curl, sockets->sockets[i], evBitmask, name); 25413498266Sopenharmony_ci if(result) 25513498266Sopenharmony_ci break; 25613498266Sopenharmony_ci } 25713498266Sopenharmony_ci } 25813498266Sopenharmony_ci return result; 25913498266Sopenharmony_ci} 26013498266Sopenharmony_ci 26113498266Sopenharmony_cistatic int testone(char *URL, int timercb, int socketcb) 26213498266Sopenharmony_ci{ 26313498266Sopenharmony_ci int res = 0; 26413498266Sopenharmony_ci CURL *curl = NULL; CURLM *m = NULL; 26513498266Sopenharmony_ci struct ReadWriteSockets sockets = {{NULL, 0, 0}, {NULL, 0, 0}}; 26613498266Sopenharmony_ci struct timeval timeout = {-1, 0}; 26713498266Sopenharmony_ci int success = 0; 26813498266Sopenharmony_ci 26913498266Sopenharmony_ci /* set the limits */ 27013498266Sopenharmony_ci max_timer_calls = timercb; 27113498266Sopenharmony_ci max_socket_calls = socketcb; 27213498266Sopenharmony_ci timer_calls = 0; /* reset the globals */ 27313498266Sopenharmony_ci socket_calls = 0; 27413498266Sopenharmony_ci 27513498266Sopenharmony_ci fprintf(stderr, "start test: %d %d\n", timercb, socketcb); 27613498266Sopenharmony_ci start_test_timing(); 27713498266Sopenharmony_ci 27813498266Sopenharmony_ci res_global_init(CURL_GLOBAL_ALL); 27913498266Sopenharmony_ci if(res) 28013498266Sopenharmony_ci return res; 28113498266Sopenharmony_ci 28213498266Sopenharmony_ci easy_init(curl); 28313498266Sopenharmony_ci 28413498266Sopenharmony_ci /* specify target */ 28513498266Sopenharmony_ci easy_setopt(curl, CURLOPT_URL, URL); 28613498266Sopenharmony_ci 28713498266Sopenharmony_ci /* go verbose */ 28813498266Sopenharmony_ci easy_setopt(curl, CURLOPT_VERBOSE, 1L); 28913498266Sopenharmony_ci 29013498266Sopenharmony_ci multi_init(m); 29113498266Sopenharmony_ci 29213498266Sopenharmony_ci multi_setopt(m, CURLMOPT_SOCKETFUNCTION, curlSocketCallback); 29313498266Sopenharmony_ci multi_setopt(m, CURLMOPT_SOCKETDATA, &sockets); 29413498266Sopenharmony_ci 29513498266Sopenharmony_ci multi_setopt(m, CURLMOPT_TIMERFUNCTION, curlTimerCallback); 29613498266Sopenharmony_ci multi_setopt(m, CURLMOPT_TIMERDATA, &timeout); 29713498266Sopenharmony_ci 29813498266Sopenharmony_ci multi_add_handle(m, curl); 29913498266Sopenharmony_ci 30013498266Sopenharmony_ci res = socket_action(m, CURL_SOCKET_TIMEOUT, 0, "timeout"); 30113498266Sopenharmony_ci if(res) 30213498266Sopenharmony_ci goto test_cleanup; 30313498266Sopenharmony_ci 30413498266Sopenharmony_ci while(!checkForCompletion(m, &success)) { 30513498266Sopenharmony_ci fd_set readSet, writeSet; 30613498266Sopenharmony_ci curl_socket_t maxFd = 0; 30713498266Sopenharmony_ci struct timeval tv = {10, 0}; 30813498266Sopenharmony_ci 30913498266Sopenharmony_ci FD_ZERO(&readSet); 31013498266Sopenharmony_ci FD_ZERO(&writeSet); 31113498266Sopenharmony_ci updateFdSet(&sockets.read, &readSet, &maxFd); 31213498266Sopenharmony_ci updateFdSet(&sockets.write, &writeSet, &maxFd); 31313498266Sopenharmony_ci 31413498266Sopenharmony_ci if(timeout.tv_sec != -1) { 31513498266Sopenharmony_ci int usTimeout = getMicroSecondTimeout(&timeout); 31613498266Sopenharmony_ci tv.tv_sec = usTimeout / 1000000; 31713498266Sopenharmony_ci tv.tv_usec = usTimeout % 1000000; 31813498266Sopenharmony_ci } 31913498266Sopenharmony_ci else if(maxFd <= 0) { 32013498266Sopenharmony_ci tv.tv_sec = 0; 32113498266Sopenharmony_ci tv.tv_usec = 100000; 32213498266Sopenharmony_ci } 32313498266Sopenharmony_ci 32413498266Sopenharmony_ci assert(maxFd); 32513498266Sopenharmony_ci select_test((int)maxFd, &readSet, &writeSet, NULL, &tv); 32613498266Sopenharmony_ci 32713498266Sopenharmony_ci /* Check the sockets for reading / writing */ 32813498266Sopenharmony_ci res = checkFdSet(m, &sockets.read, &readSet, CURL_CSELECT_IN, "read"); 32913498266Sopenharmony_ci if(res) 33013498266Sopenharmony_ci goto test_cleanup; 33113498266Sopenharmony_ci res = checkFdSet(m, &sockets.write, &writeSet, CURL_CSELECT_OUT, "write"); 33213498266Sopenharmony_ci if(res) 33313498266Sopenharmony_ci goto test_cleanup; 33413498266Sopenharmony_ci 33513498266Sopenharmony_ci if(timeout.tv_sec != -1 && getMicroSecondTimeout(&timeout) == 0) { 33613498266Sopenharmony_ci /* Curl's timer has elapsed. */ 33713498266Sopenharmony_ci res = socket_action(m, CURL_SOCKET_TIMEOUT, 0, "timeout"); 33813498266Sopenharmony_ci if(res) 33913498266Sopenharmony_ci goto test_cleanup; 34013498266Sopenharmony_ci } 34113498266Sopenharmony_ci 34213498266Sopenharmony_ci abort_on_test_timeout(); 34313498266Sopenharmony_ci } 34413498266Sopenharmony_ci 34513498266Sopenharmony_ci if(!success) { 34613498266Sopenharmony_ci fprintf(stderr, "Error getting file.\n"); 34713498266Sopenharmony_ci res = TEST_ERR_MAJOR_BAD; 34813498266Sopenharmony_ci } 34913498266Sopenharmony_ci 35013498266Sopenharmony_citest_cleanup: 35113498266Sopenharmony_ci 35213498266Sopenharmony_ci /* proper cleanup sequence */ 35313498266Sopenharmony_ci fprintf(stderr, "cleanup: %d %d\n", timercb, socketcb); 35413498266Sopenharmony_ci curl_multi_remove_handle(m, curl); 35513498266Sopenharmony_ci curl_easy_cleanup(curl); 35613498266Sopenharmony_ci curl_multi_cleanup(m); 35713498266Sopenharmony_ci curl_global_cleanup(); 35813498266Sopenharmony_ci 35913498266Sopenharmony_ci /* free local memory */ 36013498266Sopenharmony_ci free(sockets.read.sockets); 36113498266Sopenharmony_ci free(sockets.write.sockets); 36213498266Sopenharmony_ci return res; 36313498266Sopenharmony_ci} 36413498266Sopenharmony_ci 36513498266Sopenharmony_ciint test(char *URL) 36613498266Sopenharmony_ci{ 36713498266Sopenharmony_ci int rc; 36813498266Sopenharmony_ci /* rerun the same transfer multiple times and make it fail in different 36913498266Sopenharmony_ci callback calls */ 37013498266Sopenharmony_ci rc = testone(URL, 0, 0); 37113498266Sopenharmony_ci if(rc) 37213498266Sopenharmony_ci fprintf(stderr, "test 0/0 failed: %d\n", rc); 37313498266Sopenharmony_ci 37413498266Sopenharmony_ci rc = testone(URL, 1, 0); 37513498266Sopenharmony_ci if(!rc) 37613498266Sopenharmony_ci fprintf(stderr, "test 1/0 failed: %d\n", rc); 37713498266Sopenharmony_ci 37813498266Sopenharmony_ci rc = testone(URL, 2, 0); 37913498266Sopenharmony_ci if(!rc) 38013498266Sopenharmony_ci fprintf(stderr, "test 2/0 failed: %d\n", rc); 38113498266Sopenharmony_ci 38213498266Sopenharmony_ci rc = testone(URL, 0, 1); 38313498266Sopenharmony_ci if(!rc) 38413498266Sopenharmony_ci fprintf(stderr, "test 0/1 failed: %d\n", rc); 38513498266Sopenharmony_ci 38613498266Sopenharmony_ci rc = testone(URL, 0, 2); 38713498266Sopenharmony_ci if(!rc) 38813498266Sopenharmony_ci fprintf(stderr, "test 0/2 failed: %d\n", rc); 38913498266Sopenharmony_ci 39013498266Sopenharmony_ci return 0; 39113498266Sopenharmony_ci} 392