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#include "test.h" 2513498266Sopenharmony_ci 2613498266Sopenharmony_ci#include <fcntl.h> 2713498266Sopenharmony_ci 2813498266Sopenharmony_ci#include "testutil.h" 2913498266Sopenharmony_ci#include "warnless.h" 3013498266Sopenharmony_ci#include "memdebug.h" 3113498266Sopenharmony_ci 3213498266Sopenharmony_ci#define TEST_HANG_TIMEOUT 60 * 1000 3313498266Sopenharmony_ci 3413498266Sopenharmony_cistruct Sockets 3513498266Sopenharmony_ci{ 3613498266Sopenharmony_ci curl_socket_t *sockets; 3713498266Sopenharmony_ci int count; /* number of sockets actually stored in array */ 3813498266Sopenharmony_ci int max_count; /* max number of sockets that fit in allocated array */ 3913498266Sopenharmony_ci}; 4013498266Sopenharmony_ci 4113498266Sopenharmony_cistruct ReadWriteSockets 4213498266Sopenharmony_ci{ 4313498266Sopenharmony_ci struct Sockets read, write; 4413498266Sopenharmony_ci}; 4513498266Sopenharmony_ci 4613498266Sopenharmony_ci/** 4713498266Sopenharmony_ci * Remove a file descriptor from a sockets array. 4813498266Sopenharmony_ci */ 4913498266Sopenharmony_cistatic void removeFd(struct Sockets *sockets, curl_socket_t fd, int mention) 5013498266Sopenharmony_ci{ 5113498266Sopenharmony_ci int i; 5213498266Sopenharmony_ci 5313498266Sopenharmony_ci if(mention) 5413498266Sopenharmony_ci fprintf(stderr, "Remove socket fd %d\n", (int) fd); 5513498266Sopenharmony_ci 5613498266Sopenharmony_ci for(i = 0; i < sockets->count; ++i) { 5713498266Sopenharmony_ci if(sockets->sockets[i] == fd) { 5813498266Sopenharmony_ci if(i < sockets->count - 1) 5913498266Sopenharmony_ci memmove(&sockets->sockets[i], &sockets->sockets[i + 1], 6013498266Sopenharmony_ci sizeof(curl_socket_t) * (sockets->count - (i + 1))); 6113498266Sopenharmony_ci --sockets->count; 6213498266Sopenharmony_ci } 6313498266Sopenharmony_ci } 6413498266Sopenharmony_ci} 6513498266Sopenharmony_ci 6613498266Sopenharmony_ci/** 6713498266Sopenharmony_ci * Add a file descriptor to a sockets array. 6813498266Sopenharmony_ci */ 6913498266Sopenharmony_cistatic void addFd(struct Sockets *sockets, curl_socket_t fd, const char *what) 7013498266Sopenharmony_ci{ 7113498266Sopenharmony_ci /** 7213498266Sopenharmony_ci * To ensure we only have each file descriptor once, we remove it then add 7313498266Sopenharmony_ci * it again. 7413498266Sopenharmony_ci */ 7513498266Sopenharmony_ci fprintf(stderr, "Add socket fd %d for %s\n", (int) fd, what); 7613498266Sopenharmony_ci removeFd(sockets, fd, 0); 7713498266Sopenharmony_ci /* 7813498266Sopenharmony_ci * Allocate array storage when required. 7913498266Sopenharmony_ci */ 8013498266Sopenharmony_ci if(!sockets->sockets) { 8113498266Sopenharmony_ci sockets->sockets = malloc(sizeof(curl_socket_t) * 20U); 8213498266Sopenharmony_ci if(!sockets->sockets) 8313498266Sopenharmony_ci return; 8413498266Sopenharmony_ci sockets->max_count = 20; 8513498266Sopenharmony_ci } 8613498266Sopenharmony_ci else if(sockets->count + 1 > sockets->max_count) { 8713498266Sopenharmony_ci curl_socket_t *oldptr = sockets->sockets; 8813498266Sopenharmony_ci sockets->sockets = realloc(oldptr, sizeof(curl_socket_t) * 8913498266Sopenharmony_ci (sockets->max_count + 20)); 9013498266Sopenharmony_ci if(!sockets->sockets) { 9113498266Sopenharmony_ci /* cleanup in test_cleanup */ 9213498266Sopenharmony_ci sockets->sockets = oldptr; 9313498266Sopenharmony_ci return; 9413498266Sopenharmony_ci } 9513498266Sopenharmony_ci sockets->max_count += 20; 9613498266Sopenharmony_ci } 9713498266Sopenharmony_ci /* 9813498266Sopenharmony_ci * Add file descriptor to array. 9913498266Sopenharmony_ci */ 10013498266Sopenharmony_ci sockets->sockets[sockets->count] = fd; 10113498266Sopenharmony_ci ++sockets->count; 10213498266Sopenharmony_ci} 10313498266Sopenharmony_ci 10413498266Sopenharmony_ci/** 10513498266Sopenharmony_ci * Callback invoked by curl to poll reading / writing of a socket. 10613498266Sopenharmony_ci */ 10713498266Sopenharmony_cistatic int curlSocketCallback(CURL *easy, curl_socket_t s, int action, 10813498266Sopenharmony_ci void *userp, void *socketp) 10913498266Sopenharmony_ci{ 11013498266Sopenharmony_ci struct ReadWriteSockets *sockets = userp; 11113498266Sopenharmony_ci 11213498266Sopenharmony_ci (void)easy; /* unused */ 11313498266Sopenharmony_ci (void)socketp; /* unused */ 11413498266Sopenharmony_ci 11513498266Sopenharmony_ci if(action == CURL_POLL_IN || action == CURL_POLL_INOUT) 11613498266Sopenharmony_ci addFd(&sockets->read, s, "read"); 11713498266Sopenharmony_ci 11813498266Sopenharmony_ci if(action == CURL_POLL_OUT || action == CURL_POLL_INOUT) 11913498266Sopenharmony_ci addFd(&sockets->write, s, "write"); 12013498266Sopenharmony_ci 12113498266Sopenharmony_ci if(action == CURL_POLL_REMOVE) { 12213498266Sopenharmony_ci removeFd(&sockets->read, s, 1); 12313498266Sopenharmony_ci removeFd(&sockets->write, s, 0); 12413498266Sopenharmony_ci } 12513498266Sopenharmony_ci 12613498266Sopenharmony_ci return 0; 12713498266Sopenharmony_ci} 12813498266Sopenharmony_ci 12913498266Sopenharmony_ci/** 13013498266Sopenharmony_ci * Callback invoked by curl to set a timeout. 13113498266Sopenharmony_ci */ 13213498266Sopenharmony_cistatic int curlTimerCallback(CURLM *multi, long timeout_ms, void *userp) 13313498266Sopenharmony_ci{ 13413498266Sopenharmony_ci struct timeval *timeout = userp; 13513498266Sopenharmony_ci 13613498266Sopenharmony_ci (void)multi; /* unused */ 13713498266Sopenharmony_ci if(timeout_ms != -1) { 13813498266Sopenharmony_ci *timeout = tutil_tvnow(); 13913498266Sopenharmony_ci timeout->tv_usec += (int)timeout_ms * 1000; 14013498266Sopenharmony_ci } 14113498266Sopenharmony_ci else { 14213498266Sopenharmony_ci timeout->tv_sec = -1; 14313498266Sopenharmony_ci } 14413498266Sopenharmony_ci return 0; 14513498266Sopenharmony_ci} 14613498266Sopenharmony_ci 14713498266Sopenharmony_ci/** 14813498266Sopenharmony_ci * Check for curl completion. 14913498266Sopenharmony_ci */ 15013498266Sopenharmony_cistatic int checkForCompletion(CURLM *curl, int *success) 15113498266Sopenharmony_ci{ 15213498266Sopenharmony_ci int result = 0; 15313498266Sopenharmony_ci *success = 0; 15413498266Sopenharmony_ci while(1) { 15513498266Sopenharmony_ci int numMessages; 15613498266Sopenharmony_ci CURLMsg *message = curl_multi_info_read(curl, &numMessages); 15713498266Sopenharmony_ci if(!message) 15813498266Sopenharmony_ci break; 15913498266Sopenharmony_ci if(message->msg == CURLMSG_DONE) { 16013498266Sopenharmony_ci result = 1; 16113498266Sopenharmony_ci if(message->data.result == CURLE_OK) 16213498266Sopenharmony_ci *success = 1; 16313498266Sopenharmony_ci else 16413498266Sopenharmony_ci *success = 0; 16513498266Sopenharmony_ci } 16613498266Sopenharmony_ci else { 16713498266Sopenharmony_ci fprintf(stderr, "Got an unexpected message from curl: %i\n", 16813498266Sopenharmony_ci (int)message->msg); 16913498266Sopenharmony_ci result = 1; 17013498266Sopenharmony_ci *success = 0; 17113498266Sopenharmony_ci } 17213498266Sopenharmony_ci } 17313498266Sopenharmony_ci return result; 17413498266Sopenharmony_ci} 17513498266Sopenharmony_ci 17613498266Sopenharmony_cistatic int getMicroSecondTimeout(struct timeval *timeout) 17713498266Sopenharmony_ci{ 17813498266Sopenharmony_ci struct timeval now; 17913498266Sopenharmony_ci ssize_t result; 18013498266Sopenharmony_ci now = tutil_tvnow(); 18113498266Sopenharmony_ci result = (ssize_t)((timeout->tv_sec - now.tv_sec) * 1000000 + 18213498266Sopenharmony_ci timeout->tv_usec - now.tv_usec); 18313498266Sopenharmony_ci if(result < 0) 18413498266Sopenharmony_ci result = 0; 18513498266Sopenharmony_ci 18613498266Sopenharmony_ci return curlx_sztosi(result); 18713498266Sopenharmony_ci} 18813498266Sopenharmony_ci 18913498266Sopenharmony_ci/** 19013498266Sopenharmony_ci * Update a fd_set with all of the sockets in use. 19113498266Sopenharmony_ci */ 19213498266Sopenharmony_cistatic void updateFdSet(struct Sockets *sockets, fd_set* fdset, 19313498266Sopenharmony_ci curl_socket_t *maxFd) 19413498266Sopenharmony_ci{ 19513498266Sopenharmony_ci int i; 19613498266Sopenharmony_ci for(i = 0; i < sockets->count; ++i) { 19713498266Sopenharmony_ci FD_SET(sockets->sockets[i], fdset); 19813498266Sopenharmony_ci if(*maxFd < sockets->sockets[i] + 1) { 19913498266Sopenharmony_ci *maxFd = sockets->sockets[i] + 1; 20013498266Sopenharmony_ci } 20113498266Sopenharmony_ci } 20213498266Sopenharmony_ci} 20313498266Sopenharmony_ci 20413498266Sopenharmony_cistatic void notifyCurl(CURLM *curl, curl_socket_t s, int evBitmask, 20513498266Sopenharmony_ci const char *info) 20613498266Sopenharmony_ci{ 20713498266Sopenharmony_ci int numhandles = 0; 20813498266Sopenharmony_ci CURLMcode result = curl_multi_socket_action(curl, s, evBitmask, &numhandles); 20913498266Sopenharmony_ci if(result != CURLM_OK) { 21013498266Sopenharmony_ci fprintf(stderr, "Curl error on %s: %i (%s)\n", 21113498266Sopenharmony_ci info, result, curl_multi_strerror(result)); 21213498266Sopenharmony_ci } 21313498266Sopenharmony_ci} 21413498266Sopenharmony_ci 21513498266Sopenharmony_ci/** 21613498266Sopenharmony_ci * Invoke curl when a file descriptor is set. 21713498266Sopenharmony_ci */ 21813498266Sopenharmony_cistatic void checkFdSet(CURLM *curl, struct Sockets *sockets, fd_set *fdset, 21913498266Sopenharmony_ci int evBitmask, const char *name) 22013498266Sopenharmony_ci{ 22113498266Sopenharmony_ci int i; 22213498266Sopenharmony_ci for(i = 0; i < sockets->count; ++i) { 22313498266Sopenharmony_ci if(FD_ISSET(sockets->sockets[i], fdset)) { 22413498266Sopenharmony_ci notifyCurl(curl, sockets->sockets[i], evBitmask, name); 22513498266Sopenharmony_ci } 22613498266Sopenharmony_ci } 22713498266Sopenharmony_ci} 22813498266Sopenharmony_ci 22913498266Sopenharmony_ciint test(char *URL) 23013498266Sopenharmony_ci{ 23113498266Sopenharmony_ci int res = 0; 23213498266Sopenharmony_ci CURL *curl = NULL; 23313498266Sopenharmony_ci FILE *hd_src = NULL; 23413498266Sopenharmony_ci int hd; 23513498266Sopenharmony_ci struct_stat file_info; 23613498266Sopenharmony_ci CURLM *m = NULL; 23713498266Sopenharmony_ci struct ReadWriteSockets sockets = {{NULL, 0, 0}, {NULL, 0, 0}}; 23813498266Sopenharmony_ci struct timeval timeout = {-1, 0}; 23913498266Sopenharmony_ci int success = 0; 24013498266Sopenharmony_ci 24113498266Sopenharmony_ci assert(test_argc >= 5); 24213498266Sopenharmony_ci 24313498266Sopenharmony_ci start_test_timing(); 24413498266Sopenharmony_ci 24513498266Sopenharmony_ci if(!libtest_arg3) { 24613498266Sopenharmony_ci fprintf(stderr, "Usage: lib582 [url] [filename] [username]\n"); 24713498266Sopenharmony_ci return TEST_ERR_USAGE; 24813498266Sopenharmony_ci } 24913498266Sopenharmony_ci 25013498266Sopenharmony_ci hd_src = fopen(libtest_arg2, "rb"); 25113498266Sopenharmony_ci if(!hd_src) { 25213498266Sopenharmony_ci fprintf(stderr, "fopen() failed with error: %d (%s)\n", 25313498266Sopenharmony_ci errno, strerror(errno)); 25413498266Sopenharmony_ci fprintf(stderr, "Error opening file: (%s)\n", libtest_arg2); 25513498266Sopenharmony_ci return TEST_ERR_FOPEN; 25613498266Sopenharmony_ci } 25713498266Sopenharmony_ci 25813498266Sopenharmony_ci /* get the file size of the local file */ 25913498266Sopenharmony_ci hd = fstat(fileno(hd_src), &file_info); 26013498266Sopenharmony_ci if(hd == -1) { 26113498266Sopenharmony_ci /* can't open file, bail out */ 26213498266Sopenharmony_ci fprintf(stderr, "fstat() failed with error: %d (%s)\n", 26313498266Sopenharmony_ci errno, strerror(errno)); 26413498266Sopenharmony_ci fprintf(stderr, "ERROR: cannot open file (%s)\n", libtest_arg2); 26513498266Sopenharmony_ci fclose(hd_src); 26613498266Sopenharmony_ci return TEST_ERR_FSTAT; 26713498266Sopenharmony_ci } 26813498266Sopenharmony_ci fprintf(stderr, "Set to upload %d bytes\n", (int)file_info.st_size); 26913498266Sopenharmony_ci 27013498266Sopenharmony_ci res_global_init(CURL_GLOBAL_ALL); 27113498266Sopenharmony_ci if(res) { 27213498266Sopenharmony_ci fclose(hd_src); 27313498266Sopenharmony_ci return res; 27413498266Sopenharmony_ci } 27513498266Sopenharmony_ci 27613498266Sopenharmony_ci easy_init(curl); 27713498266Sopenharmony_ci 27813498266Sopenharmony_ci /* enable uploading */ 27913498266Sopenharmony_ci easy_setopt(curl, CURLOPT_UPLOAD, 1L); 28013498266Sopenharmony_ci 28113498266Sopenharmony_ci /* specify target */ 28213498266Sopenharmony_ci easy_setopt(curl, CURLOPT_URL, URL); 28313498266Sopenharmony_ci 28413498266Sopenharmony_ci /* go verbose */ 28513498266Sopenharmony_ci easy_setopt(curl, CURLOPT_VERBOSE, 1L); 28613498266Sopenharmony_ci 28713498266Sopenharmony_ci /* now specify which file to upload */ 28813498266Sopenharmony_ci easy_setopt(curl, CURLOPT_READDATA, hd_src); 28913498266Sopenharmony_ci 29013498266Sopenharmony_ci easy_setopt(curl, CURLOPT_USERPWD, libtest_arg3); 29113498266Sopenharmony_ci easy_setopt(curl, CURLOPT_SSH_PUBLIC_KEYFILE, test_argv[4]); 29213498266Sopenharmony_ci easy_setopt(curl, CURLOPT_SSH_PRIVATE_KEYFILE, test_argv[5]); 29313498266Sopenharmony_ci easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); 29413498266Sopenharmony_ci 29513498266Sopenharmony_ci easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)file_info.st_size); 29613498266Sopenharmony_ci 29713498266Sopenharmony_ci multi_init(m); 29813498266Sopenharmony_ci 29913498266Sopenharmony_ci multi_setopt(m, CURLMOPT_SOCKETFUNCTION, curlSocketCallback); 30013498266Sopenharmony_ci multi_setopt(m, CURLMOPT_SOCKETDATA, &sockets); 30113498266Sopenharmony_ci 30213498266Sopenharmony_ci multi_setopt(m, CURLMOPT_TIMERFUNCTION, curlTimerCallback); 30313498266Sopenharmony_ci multi_setopt(m, CURLMOPT_TIMERDATA, &timeout); 30413498266Sopenharmony_ci 30513498266Sopenharmony_ci multi_add_handle(m, curl); 30613498266Sopenharmony_ci 30713498266Sopenharmony_ci while(!checkForCompletion(m, &success)) { 30813498266Sopenharmony_ci fd_set readSet, writeSet; 30913498266Sopenharmony_ci curl_socket_t maxFd = 0; 31013498266Sopenharmony_ci struct timeval tv = {10, 0}; 31113498266Sopenharmony_ci 31213498266Sopenharmony_ci FD_ZERO(&readSet); 31313498266Sopenharmony_ci FD_ZERO(&writeSet); 31413498266Sopenharmony_ci updateFdSet(&sockets.read, &readSet, &maxFd); 31513498266Sopenharmony_ci updateFdSet(&sockets.write, &writeSet, &maxFd); 31613498266Sopenharmony_ci 31713498266Sopenharmony_ci if(timeout.tv_sec != -1) { 31813498266Sopenharmony_ci int usTimeout = getMicroSecondTimeout(&timeout); 31913498266Sopenharmony_ci tv.tv_sec = usTimeout / 1000000; 32013498266Sopenharmony_ci tv.tv_usec = usTimeout % 1000000; 32113498266Sopenharmony_ci } 32213498266Sopenharmony_ci else if(maxFd <= 0) { 32313498266Sopenharmony_ci tv.tv_sec = 0; 32413498266Sopenharmony_ci tv.tv_usec = 100000; 32513498266Sopenharmony_ci } 32613498266Sopenharmony_ci 32713498266Sopenharmony_ci select_test((int)maxFd, &readSet, &writeSet, NULL, &tv); 32813498266Sopenharmony_ci 32913498266Sopenharmony_ci /* Check the sockets for reading / writing */ 33013498266Sopenharmony_ci checkFdSet(m, &sockets.read, &readSet, CURL_CSELECT_IN, "read"); 33113498266Sopenharmony_ci checkFdSet(m, &sockets.write, &writeSet, CURL_CSELECT_OUT, "write"); 33213498266Sopenharmony_ci 33313498266Sopenharmony_ci if(timeout.tv_sec != -1 && getMicroSecondTimeout(&timeout) == 0) { 33413498266Sopenharmony_ci /* Curl's timer has elapsed. */ 33513498266Sopenharmony_ci notifyCurl(m, CURL_SOCKET_TIMEOUT, 0, "timeout"); 33613498266Sopenharmony_ci } 33713498266Sopenharmony_ci 33813498266Sopenharmony_ci abort_on_test_timeout(); 33913498266Sopenharmony_ci } 34013498266Sopenharmony_ci 34113498266Sopenharmony_ci if(!success) { 34213498266Sopenharmony_ci fprintf(stderr, "Error uploading file.\n"); 34313498266Sopenharmony_ci res = TEST_ERR_MAJOR_BAD; 34413498266Sopenharmony_ci } 34513498266Sopenharmony_ci 34613498266Sopenharmony_citest_cleanup: 34713498266Sopenharmony_ci 34813498266Sopenharmony_ci /* proper cleanup sequence - type PB */ 34913498266Sopenharmony_ci 35013498266Sopenharmony_ci curl_multi_remove_handle(m, curl); 35113498266Sopenharmony_ci curl_easy_cleanup(curl); 35213498266Sopenharmony_ci curl_multi_cleanup(m); 35313498266Sopenharmony_ci curl_global_cleanup(); 35413498266Sopenharmony_ci 35513498266Sopenharmony_ci /* close the local file */ 35613498266Sopenharmony_ci fclose(hd_src); 35713498266Sopenharmony_ci 35813498266Sopenharmony_ci /* free local memory */ 35913498266Sopenharmony_ci free(sockets.read.sockets); 36013498266Sopenharmony_ci free(sockets.write.sockets); 36113498266Sopenharmony_ci 36213498266Sopenharmony_ci return res; 36313498266Sopenharmony_ci} 364