libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

test_https_get_iovec.c (11604B)


      1 /*
      2   This file is part of libmicrohttpd
      3   Copyright (C) 2007-2021 Christian Grothoff
      4   Copyright (C) 2016-2022 Evgeny Grin
      5 
      6   libmicrohttpd is free software; you can redistribute it and/or modify
      7   it under the terms of the GNU General Public License as published
      8   by the Free Software Foundation; either version 3, or (at your
      9   option) any later version.
     10 
     11   libmicrohttpd is distributed in the hope that it will be useful, but
     12   WITHOUT ANY WARRANTY; without even the implied warranty of
     13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14   General Public License for more details.
     15 
     16   You should have received a copy of the GNU General Public License
     17   along with libmicrohttpd; see the file COPYING.  If not, write to the
     18   Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     19   Boston, MA 02110-1301, USA.
     20 */
     21 
     22 /**
     23  * @file test_https_get_iovec.c
     24  * @brief  Testcase for libmicrohttpd HTTPS GET operations using an iovec
     25  * @author Sagie Amir
     26  * @author Karlson2k (Evgeny Grin)
     27  * @author Lawrence Sebald
     28  */
     29 
     30 /*
     31  * This testcase is derived from the test_https_get.c testcase. This version
     32  * adds the usage of a scatter/gather array for storing the response data.
     33  */
     34 
     35 #include "platform.h"
     36 #include "microhttpd.h"
     37 #include <limits.h>
     38 #include <sys/stat.h>
     39 #include <curl/curl.h>
     40 #ifdef MHD_HTTPS_REQUIRE_GCRYPT
     41 #include <gcrypt.h>
     42 #endif /* MHD_HTTPS_REQUIRE_GCRYPT */
     43 #include "tls_test_common.h"
     44 #include "tls_test_keys.h"
     45 
     46 /* Turn any MHD_PANIC() or failing mhd_assert() reached from this
     47    test into a marked, classifiable test error (TESTING.md, P5). */
     48 #include "mhd_panic_tripwire.h"
     49 
     50 
     51 static uint16_t global_port;
     52 
     53 /* Use large enough pieces (>16KB) to test partially consumed
     54  * data as TLS doesn't take more than 16KB by a single call. */
     55 #define TESTSTR_IOVLEN 20480
     56 #define TESTSTR_IOVCNT 30
     57 #define TESTSTR_SIZE   (TESTSTR_IOVCNT * TESTSTR_IOVLEN)
     58 
     59 
     60 static void
     61 iov_free_callback (void *cls)
     62 {
     63   free (cls);
     64 }
     65 
     66 
     67 static int
     68 check_read_data (const void *ptr, size_t len)
     69 {
     70   const int *buf;
     71   size_t i;
     72 
     73   if (len % sizeof(int))
     74     return -1;
     75 
     76   buf = (const int *) ptr;
     77 
     78   for (i = 0; i < len / sizeof(int); ++i)
     79   {
     80     if (buf[i] != (int) i)
     81       return -1;
     82   }
     83 
     84   return 0;
     85 }
     86 
     87 
     88 static enum MHD_Result
     89 iovec_ahc (void *cls,
     90            struct MHD_Connection *connection,
     91            const char *url,
     92            const char *method,
     93            const char *version,
     94            const char *upload_data,
     95            size_t *upload_data_size,
     96            void **req_cls)
     97 {
     98   static int aptr;
     99   struct MHD_Response *response;
    100   enum MHD_Result ret;
    101   int *data;
    102   struct MHD_IoVec iov[TESTSTR_IOVCNT];
    103   int i;
    104   int j;
    105   (void) cls; (void) url; (void) version;          /* Unused. Silent compiler warning. */
    106   (void) upload_data; (void) upload_data_size;     /* Unused. Silent compiler warning. */
    107 
    108   if (0 != strcmp (method, MHD_HTTP_METHOD_GET))
    109     return MHD_NO;              /* unexpected method */
    110   if (&aptr != *req_cls)
    111   {
    112     /* do never respond on first call */
    113     *req_cls = &aptr;
    114     return MHD_YES;
    115   }
    116   *req_cls = NULL;                  /* reset when done */
    117 
    118   /* Create some test data. */
    119   if (NULL == (data = malloc (TESTSTR_SIZE)))
    120     return MHD_NO;
    121 
    122   for (j = 0; j < TESTSTR_IOVCNT; ++j)
    123   {
    124     int *chunk;
    125     /* Assign chunks of memory area in the reverse order
    126      * to make non-continuous set of data therefore
    127      * possible buffer overruns could be detected */
    128     chunk = data + (((TESTSTR_IOVCNT - 1) - (unsigned int) j)
    129                     * (TESTSTR_SIZE / TESTSTR_IOVCNT / sizeof(int)));
    130     iov[j].iov_base = chunk;
    131     iov[j].iov_len = TESTSTR_SIZE / TESTSTR_IOVCNT;
    132 
    133     for (i = 0; i < (int) (TESTSTR_IOVLEN / sizeof(int)); ++i)
    134       chunk[i] = i + (j * (int) (TESTSTR_IOVLEN / sizeof(int)));
    135   }
    136 
    137   response = MHD_create_response_from_iovec (iov,
    138                                              TESTSTR_IOVCNT,
    139                                              &iov_free_callback,
    140                                              data);
    141   ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
    142   MHD_destroy_response (response);
    143   return ret;
    144 }
    145 
    146 
    147 static unsigned int
    148 test_iovec_transfer (void *cls,
    149                      uint16_t port,
    150                      const char *cipher_suite,
    151                      int proto_version)
    152 {
    153   size_t len;
    154   unsigned int ret = 0;
    155   struct CBC cbc;
    156   char url[255];
    157   (void) cls;    /* Unused. Silent compiler warning. */
    158 
    159   len = TESTSTR_SIZE;
    160   if (NULL == (cbc.buf = malloc (sizeof (char) * len)))
    161   {
    162     fprintf (stderr, MHD_E_MEM);
    163     return 1;
    164   }
    165   cbc.size = len;
    166   cbc.pos = 0;
    167 
    168   if (gen_test_uri (url,
    169                     sizeof (url),
    170                     port))
    171   {
    172     ret = 1;
    173     goto cleanup;
    174   }
    175 
    176   if (CURLE_OK !=
    177       send_curl_req (url, &cbc, cipher_suite, proto_version))
    178   {
    179     ret = 1;
    180     goto cleanup;
    181   }
    182 
    183   if ((cbc.pos != TESTSTR_SIZE) ||
    184       (0 != check_read_data (cbc.buf, cbc.pos)))
    185   {
    186     fprintf (stderr, "Error: local file & received file differ.\n");
    187     ret = 1;
    188   }
    189 cleanup:
    190   free (cbc.buf);
    191   return ret;
    192 }
    193 
    194 
    195 /* perform a HTTP GET request via SSL/TLS */
    196 static unsigned int
    197 test_secure_get (FILE *test_fd,
    198                  const char *cipher_suite,
    199                  int proto_version)
    200 {
    201   unsigned int ret;
    202   struct MHD_Daemon *d;
    203   uint16_t port;
    204 
    205   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    206     port = 0;
    207   else
    208     port = 3045;
    209 
    210   d = MHD_start_daemon (MHD_USE_THREAD_PER_CONNECTION
    211                         | MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_TLS
    212                         | MHD_USE_ERROR_LOG, port,
    213                         NULL, NULL,
    214                         &iovec_ahc, NULL,
    215                         MHD_OPTION_HTTPS_MEM_KEY, srv_signed_key_pem,
    216                         MHD_OPTION_HTTPS_MEM_CERT, srv_signed_cert_pem,
    217                         MHD_OPTION_END);
    218 
    219   if (d == NULL)
    220   {
    221     fprintf (stderr, MHD_E_SERVER_INIT);
    222     return 1;
    223   }
    224   if (0 == port)
    225   {
    226     const union MHD_DaemonInfo *dinfo;
    227     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    228     if ((NULL == dinfo) || (0 == dinfo->port) )
    229     {
    230       MHD_stop_daemon (d);
    231       return 1;
    232     }
    233     port = dinfo->port;
    234   }
    235 
    236   ret = test_iovec_transfer (test_fd,
    237                              port,
    238                              cipher_suite,
    239                              proto_version);
    240 
    241   MHD_stop_daemon (d);
    242   return ret;
    243 }
    244 
    245 
    246 static enum MHD_Result
    247 ahc_empty (void *cls,
    248            struct MHD_Connection *connection,
    249            const char *url,
    250            const char *method,
    251            const char *version,
    252            const char *upload_data,
    253            size_t *upload_data_size,
    254            void **req_cls)
    255 {
    256   static int ptr;
    257   struct MHD_Response *response;
    258   enum MHD_Result ret;
    259   struct MHD_IoVec iov;
    260   (void) cls;
    261   (void) url;
    262   (void) url;
    263   (void) version;          /* Unused. Silent compiler warning. */
    264   (void) upload_data;
    265   (void) upload_data_size; /* Unused. Silent compiler warning. */
    266 
    267   if (0 != strcmp (MHD_HTTP_METHOD_GET,
    268                    method))
    269     return MHD_NO;              /* unexpected method */
    270   if (&ptr != *req_cls)
    271   {
    272     *req_cls = &ptr;
    273     return MHD_YES;
    274   }
    275   *req_cls = NULL;
    276 
    277   iov.iov_base = NULL;
    278   iov.iov_len = 0;
    279 
    280   response = MHD_create_response_from_iovec (&iov,
    281                                              1,
    282                                              NULL,
    283                                              NULL);
    284   ret = MHD_queue_response (connection,
    285                             MHD_HTTP_OK,
    286                             response);
    287   MHD_destroy_response (response);
    288   if (ret == MHD_NO)
    289   {
    290     fprintf (stderr, "Failed to queue response.\n");
    291     _exit (20);
    292   }
    293   return ret;
    294 }
    295 
    296 
    297 static int
    298 curlExcessFound (CURL *c,
    299                  curl_infotype type,
    300                  char *data,
    301                  size_t size,
    302                  void *cls)
    303 {
    304   static const char *excess_found = "Excess found";
    305   const size_t str_size = strlen (excess_found);
    306   (void) c;      /* Unused. Silence compiler warning. */
    307 
    308 #ifdef _DEBUG
    309   if ((CURLINFO_TEXT == type) ||
    310       (CURLINFO_HEADER_IN == type) ||
    311       (CURLINFO_HEADER_OUT == type))
    312     fprintf (stderr, "%.*s", (int) size, data);
    313 #endif /* _DEBUG */
    314   if ((CURLINFO_TEXT == type)
    315       && (size >= str_size)
    316       && (0 == strncmp (excess_found, data, str_size)))
    317     *(int *) cls = 1;
    318   return 0;
    319 }
    320 
    321 
    322 static unsigned int
    323 testEmptyGet (unsigned int poll_flag)
    324 {
    325   struct MHD_Daemon *d;
    326   CURL *c;
    327   char buf[2048];
    328   struct CBC cbc;
    329   CURLcode errornum;
    330   int excess_found = 0;
    331 
    332 
    333   if ( (0 == global_port) &&
    334        (MHD_NO == MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) )
    335   {
    336     global_port = 1225;
    337 
    338   }
    339 
    340   cbc.buf = buf;
    341   cbc.size = 2048;
    342   cbc.pos = 0;
    343   d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG
    344                         | poll_flag | MHD_USE_TLS,
    345                         global_port, NULL, NULL,
    346                         &ahc_empty, NULL,
    347                         MHD_OPTION_HTTPS_MEM_KEY, srv_signed_key_pem,
    348                         MHD_OPTION_HTTPS_MEM_CERT, srv_signed_cert_pem,
    349                         MHD_OPTION_END);
    350   if (d == NULL)
    351     return 4194304;
    352   if (0 == global_port)
    353   {
    354     const union MHD_DaemonInfo *dinfo;
    355     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    356     if ((NULL == dinfo) || (0 == dinfo->port) )
    357     {
    358       MHD_stop_daemon (d); return 32;
    359     }
    360     global_port = dinfo->port;
    361   }
    362   c = curl_easy_init ();
    363 #ifdef _DEBUG
    364   curl_easy_setopt (c, CURLOPT_VERBOSE, 1L);
    365 #endif
    366   curl_easy_setopt (c, CURLOPT_URL, "https://127.0.0.1/");
    367   curl_easy_setopt (c, CURLOPT_PORT, (long) global_port);
    368   curl_easy_setopt (c, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1);
    369   curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, &copyBuffer);
    370   curl_easy_setopt (c, CURLOPT_WRITEDATA, &cbc);
    371   curl_easy_setopt (c, CURLOPT_DEBUGFUNCTION, &curlExcessFound);
    372   curl_easy_setopt (c, CURLOPT_DEBUGDATA, &excess_found);
    373   curl_easy_setopt (c, CURLOPT_VERBOSE, 1L);
    374   curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L);
    375   curl_easy_setopt (c, CURLOPT_TIMEOUT, 150L);
    376   curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 150L);
    377   curl_easy_setopt (c, CURLOPT_SSL_VERIFYPEER, 0L);
    378   curl_easy_setopt (c, CURLOPT_SSL_VERIFYHOST, 0L);
    379   /* NOTE: use of CONNECTTIMEOUT without also
    380      setting NOSIGNAL results in really weird
    381      crashes on my system!*/
    382   curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L);
    383   if (CURLE_OK != (errornum = curl_easy_perform (c)))
    384   {
    385     fprintf (stderr,
    386              "curl_easy_perform failed: `%s'\n",
    387              curl_easy_strerror (errornum));
    388     curl_easy_cleanup (c);
    389     MHD_stop_daemon (d);
    390     return 8388608;
    391   }
    392   curl_easy_cleanup (c);
    393   MHD_stop_daemon (d);
    394   if (cbc.pos != 0)
    395     return 16777216;
    396   if (excess_found)
    397     return 33554432;
    398   return 0;
    399 }
    400 
    401 
    402 int
    403 main (int argc, char *const *argv)
    404 {
    405   unsigned int errorCount = 0;
    406   (void) argc; (void) argv;   /* Unused. Silent compiler warning. */
    407 
    408 #ifdef MHD_HTTPS_REQUIRE_GCRYPT
    409   gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0);
    410 #ifdef GCRYCTL_INITIALIZATION_FINISHED
    411   gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
    412 #endif
    413 #endif /* MHD_HTTPS_REQUIRE_GCRYPT */
    414   if (! testsuite_curl_global_init ())
    415     return 99;
    416   if (NULL == curl_version_info (CURLVERSION_NOW)->ssl_version)
    417   {
    418     fprintf (stderr, "Curl does not support SSL.  Cannot run the test.\n");
    419     curl_global_cleanup ();
    420     return 77;
    421   }
    422 
    423   errorCount +=
    424     test_secure_get (NULL, NULL, CURL_SSLVERSION_DEFAULT);
    425   errorCount += testEmptyGet (0);
    426   curl_global_cleanup ();
    427 
    428   return errorCount != 0 ? 1 : 0;
    429 }