lib582.c (9754B)
1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 9 * 10 * This software is licensed as described in the file COPYING, which 11 * you should have received as part of this distribution. The terms 12 * are also available at https://curl.se/docs/copyright.html. 13 * 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 * copies of the Software, and permit persons to whom the Software is 16 * furnished to do so, under the terms of the COPYING file. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 * SPDX-License-Identifier: curl 22 * 23 ***************************************************************************/ 24 #include "first.h" 25 26 #include "memdebug.h" 27 28 struct t582_Sockets { 29 curl_socket_t *sockets; 30 int count; /* number of sockets actually stored in array */ 31 int max_count; /* max number of sockets that fit in allocated array */ 32 }; 33 34 struct t582_ReadWriteSockets { 35 struct t582_Sockets read, write; 36 }; 37 38 /** 39 * Remove a file descriptor from a sockets array. 40 */ 41 static void t582_removeFd(struct t582_Sockets *sockets, curl_socket_t fd, 42 int mention) 43 { 44 int i; 45 46 if(mention) 47 curl_mfprintf(stderr, "Remove socket fd %d\n", (int) fd); 48 49 for(i = 0; i < sockets->count; ++i) { 50 if(sockets->sockets[i] == fd) { 51 if(i < sockets->count - 1) 52 memmove(&sockets->sockets[i], &sockets->sockets[i + 1], 53 sizeof(curl_socket_t) * (sockets->count - (i + 1))); 54 --sockets->count; 55 } 56 } 57 } 58 59 /** 60 * Add a file descriptor to a sockets array. 61 */ 62 static void t582_addFd(struct t582_Sockets *sockets, curl_socket_t fd, 63 const char *what) 64 { 65 /** 66 * To ensure we only have each file descriptor once, we remove it then add 67 * it again. 68 */ 69 curl_mfprintf(stderr, "Add socket fd %d for %s\n", (int) fd, what); 70 t582_removeFd(sockets, fd, 0); 71 /* 72 * Allocate array storage when required. 73 */ 74 if(!sockets->sockets) { 75 sockets->sockets = malloc(sizeof(curl_socket_t) * 20U); 76 if(!sockets->sockets) 77 return; 78 sockets->max_count = 20; 79 } 80 else if(sockets->count >= sockets->max_count) { 81 /* this can't happen in normal cases */ 82 curl_mfprintf(stderr, "too many file handles error\n"); 83 exit(2); 84 } 85 /* 86 * Add file descriptor to array. 87 */ 88 sockets->sockets[sockets->count] = fd; 89 ++sockets->count; 90 } 91 92 /** 93 * Callback invoked by curl to poll reading / writing of a socket. 94 */ 95 static int t582_curlSocketCallback(CURL *easy, curl_socket_t s, int action, 96 void *userp, void *socketp) 97 { 98 struct t582_ReadWriteSockets *sockets = userp; 99 100 (void)easy; /* unused */ 101 (void)socketp; /* unused */ 102 103 if(action == CURL_POLL_IN || action == CURL_POLL_INOUT) 104 t582_addFd(&sockets->read, s, "read"); 105 106 if(action == CURL_POLL_OUT || action == CURL_POLL_INOUT) 107 t582_addFd(&sockets->write, s, "write"); 108 109 if(action == CURL_POLL_REMOVE) { 110 t582_removeFd(&sockets->read, s, 1); 111 t582_removeFd(&sockets->write, s, 0); 112 } 113 114 return 0; 115 } 116 117 /** 118 * Callback invoked by curl to set a timeout. 119 */ 120 static int t582_curlTimerCallback(CURLM *multi, long timeout_ms, void *userp) 121 { 122 struct curltime *timeout = userp; 123 124 (void)multi; /* unused */ 125 if(timeout_ms != -1) { 126 *timeout = curlx_now(); 127 timeout->tv_usec += (int)timeout_ms * 1000; 128 } 129 else { 130 timeout->tv_sec = -1; 131 } 132 return 0; 133 } 134 135 /** 136 * Check for curl completion. 137 */ 138 static int t582_checkForCompletion(CURLM *curl, int *success) 139 { 140 int result = 0; 141 *success = 0; 142 while(1) { 143 int numMessages; 144 CURLMsg *message = curl_multi_info_read(curl, &numMessages); 145 if(!message) 146 break; 147 if(message->msg == CURLMSG_DONE) { 148 result = 1; 149 if(message->data.result == CURLE_OK) 150 *success = 1; 151 else 152 *success = 0; 153 } 154 else { 155 curl_mfprintf(stderr, "Got an unexpected message from curl: %i\n", 156 (int)message->msg); 157 result = 1; 158 *success = 0; 159 } 160 } 161 return result; 162 } 163 164 static int t582_getMicroSecondTimeout(struct curltime *timeout) 165 { 166 struct curltime now; 167 ssize_t result; 168 now = curlx_now(); 169 result = (ssize_t)((timeout->tv_sec - now.tv_sec) * 1000000 + 170 timeout->tv_usec - now.tv_usec); 171 if(result < 0) 172 result = 0; 173 174 return curlx_sztosi(result); 175 } 176 177 /** 178 * Update a fd_set with all of the sockets in use. 179 */ 180 static void t582_updateFdSet(struct t582_Sockets *sockets, fd_set* fdset, 181 curl_socket_t *maxFd) 182 { 183 int i; 184 for(i = 0; i < sockets->count; ++i) { 185 #if defined(__DJGPP__) 186 #pragma GCC diagnostic push 187 #pragma GCC diagnostic ignored "-Warith-conversion" 188 #endif 189 FD_SET(sockets->sockets[i], fdset); 190 #if defined(__DJGPP__) 191 #pragma GCC diagnostic pop 192 #endif 193 if(*maxFd < sockets->sockets[i] + 1) { 194 *maxFd = sockets->sockets[i] + 1; 195 } 196 } 197 } 198 199 static void notifyCurl(CURLM *curl, curl_socket_t s, int evBitmask, 200 const char *info) 201 { 202 int numhandles = 0; 203 CURLMcode result = curl_multi_socket_action(curl, s, evBitmask, &numhandles); 204 if(result != CURLM_OK) { 205 curl_mfprintf(stderr, "Curl error on %s (%i) %s\n", 206 info, result, curl_multi_strerror(result)); 207 } 208 } 209 210 /** 211 * Invoke curl when a file descriptor is set. 212 */ 213 static void t582_checkFdSet(CURLM *curl, struct t582_Sockets *sockets, 214 fd_set *fdset, int evBitmask, const char *name) 215 { 216 int i; 217 for(i = 0; i < sockets->count; ++i) { 218 if(FD_ISSET(sockets->sockets[i], fdset)) { 219 notifyCurl(curl, sockets->sockets[i], evBitmask, name); 220 } 221 } 222 } 223 224 static CURLcode test_lib582(char *URL) 225 { 226 CURLcode res = CURLE_OK; 227 CURL *curl = NULL; 228 FILE *hd_src = NULL; 229 int hd; 230 struct_stat file_info; 231 CURLM *m = NULL; 232 struct t582_ReadWriteSockets sockets = {{NULL, 0, 0}, {NULL, 0, 0}}; 233 int success = 0; 234 struct curltime timeout = {0}; 235 timeout.tv_sec = (time_t)-1; 236 237 assert(test_argc >= 5); 238 239 start_test_timing(); 240 241 if(!libtest_arg3) { 242 curl_mfprintf(stderr, "Usage: lib582 [url] [filename] [username]\n"); 243 return TEST_ERR_USAGE; 244 } 245 246 hd_src = fopen(libtest_arg2, "rb"); 247 if(!hd_src) { 248 curl_mfprintf(stderr, "fopen() failed with error (%d) %s\n", 249 errno, strerror(errno)); 250 curl_mfprintf(stderr, "Error opening file '%s'\n", libtest_arg2); 251 return TEST_ERR_FOPEN; 252 } 253 254 /* get the file size of the local file */ 255 #ifdef UNDER_CE 256 hd = stat(libtest_arg2, &file_info); 257 #else 258 hd = fstat(fileno(hd_src), &file_info); 259 #endif 260 if(hd == -1) { 261 /* can't open file, bail out */ 262 curl_mfprintf(stderr, "fstat() failed with error (%d) %s\n", 263 errno, strerror(errno)); 264 curl_mfprintf(stderr, "Error opening file '%s'\n", libtest_arg2); 265 fclose(hd_src); 266 return TEST_ERR_FSTAT; 267 } 268 curl_mfprintf(stderr, "Set to upload %d bytes\n", (int)file_info.st_size); 269 270 res_global_init(CURL_GLOBAL_ALL); 271 if(res != CURLE_OK) { 272 fclose(hd_src); 273 return res; 274 } 275 276 easy_init(curl); 277 278 /* enable uploading */ 279 easy_setopt(curl, CURLOPT_UPLOAD, 1L); 280 281 /* specify target */ 282 easy_setopt(curl, CURLOPT_URL, URL); 283 284 /* go verbose */ 285 easy_setopt(curl, CURLOPT_VERBOSE, 1L); 286 287 /* now specify which file to upload */ 288 easy_setopt(curl, CURLOPT_READDATA, hd_src); 289 290 easy_setopt(curl, CURLOPT_USERPWD, libtest_arg3); 291 easy_setopt(curl, CURLOPT_SSH_PUBLIC_KEYFILE, test_argv[4]); 292 easy_setopt(curl, CURLOPT_SSH_PRIVATE_KEYFILE, test_argv[5]); 293 easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); 294 295 easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)file_info.st_size); 296 297 multi_init(m); 298 299 multi_setopt(m, CURLMOPT_SOCKETFUNCTION, t582_curlSocketCallback); 300 multi_setopt(m, CURLMOPT_SOCKETDATA, &sockets); 301 302 multi_setopt(m, CURLMOPT_TIMERFUNCTION, t582_curlTimerCallback); 303 multi_setopt(m, CURLMOPT_TIMERDATA, &timeout); 304 305 multi_add_handle(m, curl); 306 307 while(!t582_checkForCompletion(m, &success)) { 308 fd_set readSet, writeSet; 309 curl_socket_t maxFd = 0; 310 struct timeval tv = {0}; 311 tv.tv_sec = 10; 312 313 FD_ZERO(&readSet); 314 FD_ZERO(&writeSet); 315 t582_updateFdSet(&sockets.read, &readSet, &maxFd); 316 t582_updateFdSet(&sockets.write, &writeSet, &maxFd); 317 318 if(timeout.tv_sec != (time_t)-1) { 319 int usTimeout = t582_getMicroSecondTimeout(&timeout); 320 tv.tv_sec = usTimeout / 1000000; 321 tv.tv_usec = usTimeout % 1000000; 322 } 323 else if(maxFd <= 0) { 324 tv.tv_sec = 0; 325 tv.tv_usec = 100000; 326 } 327 328 select_test((int)maxFd, &readSet, &writeSet, NULL, &tv); 329 330 /* Check the sockets for reading / writing */ 331 t582_checkFdSet(m, &sockets.read, &readSet, CURL_CSELECT_IN, "read"); 332 t582_checkFdSet(m, &sockets.write, &writeSet, CURL_CSELECT_OUT, "write"); 333 334 if(timeout.tv_sec != (time_t)-1 && 335 t582_getMicroSecondTimeout(&timeout) == 0) { 336 /* Curl's timer has elapsed. */ 337 notifyCurl(m, CURL_SOCKET_TIMEOUT, 0, "timeout"); 338 } 339 340 abort_on_test_timeout(); 341 } 342 343 if(!success) { 344 curl_mfprintf(stderr, "Error uploading file.\n"); 345 res = TEST_ERR_MAJOR_BAD; 346 } 347 348 test_cleanup: 349 350 /* proper cleanup sequence - type PB */ 351 352 curl_multi_remove_handle(m, curl); 353 curl_easy_cleanup(curl); 354 curl_multi_cleanup(m); 355 curl_global_cleanup(); 356 357 /* close the local file */ 358 fclose(hd_src); 359 360 /* free local memory */ 361 free(sockets.read.sockets); 362 free(sockets.write.sockets); 363 364 return res; 365 }