xref: /third_party/curl/tests/libtest/lib582.c (revision 13498266)
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