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