libmicrohttpd

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

test_get.c (53574B)


      1 /*
      2      This file is part of libmicrohttpd
      3      Copyright (C) 2007, 2008 Christian Grothoff
      4      Copyright (C) 2016-2022 Evgeny Grin (Karlson2k)
      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 2, 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 testzzuf/test_get.c
     24  * @brief  Several testcases for libmicrohttpd with input fuzzing
     25  * @author Christian Grothoff
     26  * @author Karlson2k (Evgeny Grin)
     27  */
     28 
     29 #include "platform.h"
     30 #include <curl/curl.h>
     31 #include <microhttpd.h>
     32 #include <stdlib.h>
     33 #include <string.h>
     34 #include <time.h>
     35 #include <errno.h>
     36 
     37 #ifndef WINDOWS
     38 #include <unistd.h>
     39 #endif
     40 
     41 #include "mhd_debug_funcs.h"
     42 #include "mhd_zzuf_common.h"
     43 #include "test_helpers.h"
     44 
     45 #ifndef MHD_STATICSTR_LEN_
     46 /**
     47  * Determine length of static string / macro strings at compile time.
     48  */
     49 #define MHD_STATICSTR_LEN_(macro) (sizeof(macro) / sizeof(char) - 1)
     50 #endif /* ! MHD_STATICSTR_LEN_ */
     51 
     52 #ifndef CURL_VERSION_BITS
     53 #define CURL_VERSION_BITS(x,y,z) ((x) << 16 | (y) << 8 | (z))
     54 #endif /* ! CURL_VERSION_BITS */
     55 #ifndef CURL_AT_LEAST_VERSION
     56 #define CURL_AT_LEAST_VERSION(x,y,z) \
     57   (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS (x, y, z))
     58 #endif /* ! CURL_AT_LEAST_VERSION */
     59 
     60 /**
     61  * A larger loop count will run more random tests --
     62  * which would be good, except that it may take too
     63  * long for most user's patience.  So this small
     64  * value is the default.
     65  * Can be redefined by CPPFLAGS=-DLOOP_COUNT=123
     66  */
     67 #ifndef LOOP_COUNT
     68 #ifndef _MHD_VHEAVY_TESTS
     69 #define LOOP_COUNT 10
     70 #else  /* ! _MHD_HEAVY_TESTS */
     71 #define LOOP_COUNT 200
     72 #endif /* ! _MHD_HEAVY_TESTS */
     73 #endif /* LOOP_COUNT */
     74 
     75 #ifdef _DEBUG
     76 /* Uncomment the next line (or use CPPFLAGS) to see all request and response bodies in log */
     77 /* #define TEST_PRINT_BODY */
     78 /* Uncomment the next line (or use CPPFLAGS) to see all request bodies as they are sent by libcurl */
     79 /* #define TEST_PRINT_BODY_RQ 1 */
     80 /* Uncomment the next line (or use CPPFLAGS) to see all request bodies as they are received by libcurl */
     81 /* #define TEST_PRINT_BODY_RP 1 */
     82 #endif /* _DEBUG */
     83 
     84 #define MHD_TIMEOUT 2
     85 
     86 #define CURL_TIMEOUT 5
     87 
     88 /* Global test parameters */
     89 static int oneone;
     90 static int dry_run;
     91 static int use_get;
     92 static int use_get_chunked;
     93 static int use_put;
     94 static int use_put_large;
     95 static int use_put_chunked;
     96 static int use_post;
     97 static int use_post_form;
     98 static int use_long_header;
     99 static int use_long_uri;
    100 static int use_close;
    101 static int run_with_socat;
    102 /**
    103  * Non-zero if this program sweeps the matrix of "hostile" daemon options
    104  * (small connection memory pools, small connection limits, short timeouts
    105  * and the various client discipline levels) instead of using the single
    106  * "plain" set of daemon options.
    107  * Derived from the "_hostile" marker in the program name.
    108  */
    109 static int use_hostile_opts;
    110 
    111 /**
    112  * The daemon option profile that is currently in use.
    113  */
    114 static const struct zzuf_opt_profile *cur_profile;
    115 
    116 /**
    117  * The connection timeout that the current daemon has been started with.
    118  */
    119 static unsigned int expected_conn_timeout = MHD_TIMEOUT;
    120 
    121 #define TEST_BASE_URI "http:/" "/127.0.0.1/test_uri"
    122 #define TEST_BASE_URI_SOCAT "http:/" "/127.0.0.121/test_uri"
    123 
    124 #define SOCAT_PORT 10121
    125 
    126 #define TEST_BASE_PORT 4010
    127 
    128 #define EMPTY_PAGE "Empty page."
    129 #define EMPTY_PAGE_ALT "Alternative empty page."
    130 #define METHOD_NOT_SUPPORTED "HTTP method is not supported."
    131 #define POST_DATA_BROKEN "The POST request is ill-formed."
    132 
    133 #define POST_KEY1 "test"
    134 #define POST_VALUE1 "test_post"
    135 #define POST_KEY2 "library"
    136 #define POST_VALUE2 "GNU libmicrohttpd"
    137 #define POST_URLENC_DATA \
    138   POST_KEY1 "=" POST_VALUE1 "&" POST_KEY2 "=" "GNU%20libmicrohttpd"
    139 
    140 #define PUT_NORMAL_SIZE 11
    141 /* Does not need to be very large as MHD buffer will be made smaller anyway */
    142 #define PUT_LARGE_SIZE (4 * 1024)
    143 /* The length of "very long" URI and header strings. MHD uses smaller buffer. */
    144 #define TEST_STRING_VLONG_LEN 8 * 1024
    145 
    146 
    147 #if ! CURL_AT_LEAST_VERSION (7,56,0)
    148 #define TEST_USE_STATIC_POST_DATA 1
    149 static struct curl_httppost *post_first;
    150 static struct curl_httppost *post_last;
    151 #endif /* ! CURL_AT_LEAST_VERSION(7,56,0) */
    152 
    153 static struct curl_slist *libcurl_long_header;
    154 
    155 /**
    156  * Initialise long header for libcurl
    157  *
    158  * @return non-zero if succeed,
    159  *         zero if failed
    160  */
    161 static int
    162 long_header_init (void)
    163 {
    164   char *buf;
    165 
    166   buf = malloc (TEST_STRING_VLONG_LEN + 1);
    167   if (NULL == buf)
    168   {
    169     fprintf (stderr, "malloc() failed "
    170              "at line %d.\n", (int) __LINE__);
    171     return 0;
    172   }
    173   buf[TEST_STRING_VLONG_LEN] = 0;
    174   buf[0] = 'A';
    175   memset (buf + 1, 'a', TEST_STRING_VLONG_LEN / 2 - 2);
    176   buf[TEST_STRING_VLONG_LEN / 2 - 1] = ':';
    177   buf[TEST_STRING_VLONG_LEN / 2] = ' ';
    178   memset (buf + TEST_STRING_VLONG_LEN / 2 + 1, 'c',
    179           TEST_STRING_VLONG_LEN / 2 - 1);
    180   libcurl_long_header = curl_slist_append (NULL, buf);
    181   free (buf);
    182   if (NULL != libcurl_long_header)
    183     return ! 0; /* Success exit point */
    184 
    185   fprintf (stderr, "curl_slist_append() failed "
    186            "at line %d.\n", (int) __LINE__);
    187   return 0; /* Failure exit point */
    188 }
    189 
    190 
    191 /**
    192  * Globally initialise test environment
    193  * @return non-zero if succeed,
    194  *         zero if failed
    195  */
    196 static int
    197 test_global_init (void)
    198 {
    199   libcurl_long_header = NULL;
    200   if (CURLE_OK != curl_global_init (CURL_GLOBAL_WIN32))
    201   {
    202     fprintf (stderr, "curl_global_init() failed "
    203              "at line %d.\n", (int) __LINE__);
    204     return 0;
    205   }
    206 
    207   if (long_header_init ())
    208   {
    209 #ifndef TEST_USE_STATIC_POST_DATA
    210     return 1; /* Success exit point */
    211 #else  /* ! TEST_USE_STATIC_POST_DATA */
    212     post_first = NULL;
    213     post_last = NULL;
    214     if ((CURL_FORMADD_OK !=
    215          curl_formadd (&post_first, &post_last,
    216                        CURLFORM_PTRNAME, POST_KEY1,
    217                        CURLFORM_NAMELENGTH,
    218                        (long) MHD_STATICSTR_LEN_ (POST_KEY1),
    219                        CURLFORM_PTRCONTENTS, POST_VALUE1,
    220 #if CURL_AT_LEAST_VERSION (7,46,0)
    221                        CURLFORM_CONTENTLEN,
    222                        (curl_off_t) MHD_STATICSTR_LEN_ (POST_VALUE1),
    223 #else  /* ! CURL_AT_LEAST_VERSION(7,46,0) */
    224                        CURLFORM_CONTENTSLENGTH,
    225                        (long) MHD_STATICSTR_LEN_ (POST_VALUE1),
    226 #endif /* ! CURL_AT_LEAST_VERSION(7,46,0) */
    227                        CURLFORM_END)) ||
    228         (CURL_FORMADD_OK !=
    229          curl_formadd (&post_first, &post_last,
    230                        CURLFORM_PTRNAME, POST_KEY2,
    231                        CURLFORM_NAMELENGTH,
    232                        (long) MHD_STATICSTR_LEN_ (POST_KEY2),
    233                        CURLFORM_PTRCONTENTS, POST_VALUE2,
    234 #if CURL_AT_LEAST_VERSION (7,46,0)
    235                        CURLFORM_CONTENTLEN,
    236                        (curl_off_t) MHD_STATICSTR_LEN_ (POST_VALUE2),
    237 #else  /* ! CURL_AT_LEAST_VERSION(7,46,0) */
    238                        CURLFORM_CONTENTSLENGTH,
    239                        (long) MHD_STATICSTR_LEN_ (POST_VALUE2),
    240 #endif /* ! CURL_AT_LEAST_VERSION(7,46,0) */
    241                        CURLFORM_END)))
    242       fprintf (stderr, "curl_formadd() failed "
    243                "at line %d.\n", (int) __LINE__);
    244     else
    245       return 1; /* Success exit point */
    246 
    247     if (NULL != post_first)
    248       curl_formfree (post_first);
    249     curl_slist_free_all (libcurl_long_header);
    250 #endif /* ! CURL_AT_LEAST_VERSION(7,56,0) */
    251   }
    252   curl_global_cleanup ();
    253   return 0; /* Failure exit point */
    254 }
    255 
    256 
    257 /**
    258  * Globally de-initialise test environment
    259  */
    260 static void
    261 test_global_deinit (void)
    262 {
    263 #ifdef TEST_USE_STATIC_POST_DATA
    264   curl_formfree (post_first);
    265 #endif /* TEST_USE_STATIC_POST_DATA */
    266   curl_global_cleanup ();
    267   if (NULL != libcurl_long_header)
    268     curl_slist_free_all (libcurl_long_header);
    269 }
    270 
    271 
    272 /**
    273  * libcurl callback parameters for uploads, downloads and debug callbacks
    274  */
    275 struct CBC
    276 {
    277   /* Upload members */
    278   size_t up_pos;
    279   size_t up_size;
    280   /* Download members */
    281   char *dn_buf;
    282   size_t dn_pos;
    283   size_t dn_buf_size;
    284   /* Debug callback members */
    285   unsigned int excess_found;
    286 };
    287 
    288 static void
    289 initCBC (struct CBC *libcurlcbc, char *dn_buf, size_t dn_buf_size)
    290 {
    291   libcurlcbc->up_pos = 0;
    292   if (use_put_large)
    293     libcurlcbc->up_size = PUT_LARGE_SIZE;
    294   else if (use_put)
    295     libcurlcbc->up_size = PUT_NORMAL_SIZE;
    296   else
    297     libcurlcbc->up_size = 0;
    298   libcurlcbc->dn_buf = dn_buf;
    299   libcurlcbc->dn_pos = 0;
    300   libcurlcbc->dn_buf_size = dn_buf_size;
    301   libcurlcbc->excess_found = 0;
    302 }
    303 
    304 
    305 static void
    306 resetCBC (struct CBC *libcurlcbc)
    307 {
    308   libcurlcbc->up_pos = 0;
    309   libcurlcbc->dn_pos = 0;
    310 }
    311 
    312 
    313 static size_t
    314 putBuffer (void *stream, size_t item_size, size_t nitems, void *ctx)
    315 {
    316   size_t to_fill;
    317   size_t i;
    318   struct CBC *cbc = ctx;
    319 
    320   to_fill = cbc->up_size - cbc->up_pos;
    321   /* Skip overflow check as the return value is valid anyway */
    322   if (use_put_chunked)
    323   {
    324     /* Send data as several chunks */
    325     if (to_fill > cbc->up_size / 3)
    326       to_fill = cbc->up_size / 3;
    327   }
    328   if (to_fill > item_size * nitems)
    329     to_fill = item_size * nitems;
    330 
    331   /* Avoid libcurl magic numbers */
    332 #ifdef CURL_READFUNC_PAUSE
    333   if (CURL_READFUNC_ABORT == to_fill)
    334     to_fill -= 2;
    335 #endif /* CURL_READFUNC_PAUSE */
    336 #ifdef CURL_READFUNC_ABORT
    337   if (CURL_READFUNC_ABORT == to_fill)
    338     --to_fill;
    339 #endif /* CURL_READFUNC_ABORT */
    340   for (i = 0; i < to_fill; ++i)
    341     ((char *) stream)[i] = 'a' + (char) ((cbc->up_pos + i)
    342                                          % (unsigned char) ('z' - 'a' + 1));
    343 
    344   cbc->up_pos += to_fill;
    345   return to_fill;
    346 }
    347 
    348 
    349 static size_t
    350 copyBuffer (void *ptr, size_t size, size_t nmemb, void *ctx)
    351 {
    352   struct CBC *cbc = ctx;
    353 
    354   if (cbc->dn_pos + size * nmemb > cbc->dn_buf_size)
    355     return 0;                   /* overflow */
    356   memcpy (&cbc->dn_buf[cbc->dn_pos], ptr, size * nmemb);
    357   cbc->dn_pos += size * nmemb;
    358   return size * nmemb;
    359 }
    360 
    361 
    362 #define TEST_MAGIC_MARKER0 0xFEE1C0DE
    363 #define TEST_MAGIC_MARKER1 (TEST_MAGIC_MARKER0 + 1)
    364 #define TEST_MAGIC_MARKER2 (TEST_MAGIC_MARKER0 + 2)
    365 
    366 struct content_cb_param_strct
    367 {
    368   unsigned int magic0;    /**< Must have TEST_MAGIC_MARKER0 value */
    369   struct MHD_Response *response; /**< The pointer to the response structure */
    370 };
    371 
    372 /**
    373  * MHD content reader callback that returns
    374  * data in chunks.
    375  */
    376 static ssize_t
    377 content_cb (void *cls, uint64_t pos, char *buf, size_t max)
    378 {
    379   struct content_cb_param_strct *param = (struct content_cb_param_strct *) cls;
    380   size_t fill_size;
    381 
    382   if ((unsigned int) TEST_MAGIC_MARKER0 != param->magic0)
    383   {
    384     fprintf (stderr, "Wrong cls pointer "
    385              "at line %d.\n", (int) __LINE__);
    386     fflush (stderr);
    387     abort ();
    388   }
    389 
    390   if (pos >= 128 * 10)
    391   {
    392     if (MHD_YES !=
    393         MHD_add_response_footer (param->response, "Footer", "working"))
    394     {
    395       fprintf (stderr, "MHD_add_response_footer() failed "
    396                "at line %d.\n", (int) __LINE__);
    397       fflush (stderr);
    398       abort ();
    399     }
    400     return MHD_CONTENT_READER_END_OF_STREAM;
    401   }
    402 
    403   if (128 > max)
    404     fill_size = 128;
    405   else
    406     fill_size = max;
    407   memset (buf, 'A' + (char) (unsigned int) (pos / 128), fill_size);
    408 
    409   return (ssize_t) fill_size;
    410 }
    411 
    412 
    413 /**
    414  * Deallocate memory for callback cls.
    415  */
    416 static void
    417 crcf (void *ptr)
    418 {
    419   free (ptr);
    420 }
    421 
    422 
    423 struct req_process_strct
    424 {
    425   unsigned int magic2;   /**< Must have TEST_MAGIC_MARKER2 value */
    426   int is_static;         /**< Non-zero if statically allocated, zero if malloc()'ed */
    427   struct MHD_PostProcessor *postprocsr;
    428   unsigned int post_data_sum;
    429 };
    430 
    431 static enum MHD_Result
    432 post_iterator (void *cls,
    433                enum MHD_ValueKind kind,
    434                const char *key,
    435                const char *filename,
    436                const char *content_type,
    437                const char *transfer_encoding,
    438                const char *value, uint64_t off, size_t size)
    439 {
    440   struct req_process_strct *param = (struct req_process_strct *) cls;
    441   size_t i;
    442 
    443   (void) filename; (void) content_type; (void) transfer_encoding;
    444   (void) off; /* Unused. Mute compiler warnings. */
    445 
    446   if (TEST_MAGIC_MARKER2 != param->magic2)
    447   {
    448     fprintf (stderr, "The 'param->magic2' has wrong value "
    449              "at line %d.\n", (int) __LINE__);
    450     abort ();
    451   }
    452 
    453   if (MHD_POSTDATA_KIND != kind)
    454   {
    455     fprintf (stderr, "The 'kind' parameter has wrong value "
    456              "at line %d.\n", (int) __LINE__);
    457     abort ();
    458   }
    459 
    460   if (NULL != key)
    461     param->post_data_sum += (unsigned int) strlen (key);
    462 
    463   for (i = 0; size > i; ++i)
    464     param->post_data_sum += (unsigned int) (unsigned char) value[i];
    465 
    466   return MHD_YES;
    467 }
    468 
    469 
    470 static void
    471 free_req_pr_data (struct req_process_strct *pr_data)
    472 {
    473   if (NULL == pr_data)
    474     return;
    475   if (TEST_MAGIC_MARKER2 != pr_data->magic2)
    476   {
    477     fprintf (stderr, "The 'pr_data->magic2' has wrong value "
    478              "at line %d.\n", (int) __LINE__);
    479     abort ();
    480   }
    481   if (pr_data->is_static)
    482   {
    483     if (NULL != pr_data->postprocsr)
    484     {
    485       fprintf (stderr, "The 'pr_data->postprocsr' has wrong value "
    486                "at line %d.\n", (int) __LINE__);
    487       abort ();
    488     }
    489     return;
    490   }
    491   if (NULL != pr_data->postprocsr)
    492     MHD_destroy_post_processor (pr_data->postprocsr);
    493   pr_data->postprocsr = NULL;
    494   free (pr_data);
    495 }
    496 
    497 
    498 struct ahc_param_strct
    499 {
    500   unsigned int magic1;   /**< Must have TEST_MAGIC_MARKER1 value */
    501   unsigned int err_flag; /**< Non-zero if any error is encountered */
    502   unsigned int num_replies; /**< The number of replies sent for the current request */
    503 };
    504 
    505 static enum MHD_Result
    506 send_error_response (struct MHD_Connection *connection,
    507                      struct ahc_param_strct *param,
    508                      unsigned int status_code,
    509                      const char *static_text,
    510                      const size_t static_text_len)
    511 {
    512   struct MHD_Response *response;
    513   response =
    514     MHD_create_response_from_buffer_static (static_text_len,
    515                                             static_text);
    516   if (NULL != response)
    517   {
    518     if (MHD_YES == MHD_add_response_header (response,
    519                                             MHD_HTTP_HEADER_CONNECTION,
    520                                             "close"))
    521     {
    522       if (MHD_YES == MHD_queue_response (connection, status_code, response))
    523       {
    524         MHD_destroy_response (response);
    525         return MHD_YES; /* Success exit point */
    526       }
    527       else
    528         fprintf (stderr, "MHD_queue_response() failed "
    529                  "at line %d.\n", (int) __LINE__);
    530     }
    531     else
    532       fprintf (stderr, "MHD_add_response_header() failed "
    533                "at line %d.\n", (int) __LINE__);
    534     MHD_destroy_response (response);
    535   }
    536   else
    537     fprintf (stderr, "MHD_create_response_from_callback() failed "
    538              "at line %d.\n", (int) __LINE__);
    539 
    540   param->err_flag = 1;
    541   return MHD_NO; /* Failure exit point */
    542 }
    543 
    544 
    545 static enum MHD_Result
    546 ahc_check (void *cls,
    547            struct MHD_Connection *connection,
    548            const char *url,
    549            const char *method,
    550            const char *version,
    551            const char *upload_data, size_t *upload_data_size,
    552            void **req_cls)
    553 {
    554   static struct req_process_strct static_req_pr_data = {
    555     TEST_MAGIC_MARKER2, ! 0, NULL, 0
    556   };
    557   struct req_process_strct *req_pr_data;
    558   struct ahc_param_strct *param = (struct ahc_param_strct *) cls;
    559   struct MHD_Response *response;
    560   enum MHD_Result ret;
    561   unsigned char data_sum;
    562   int is_post_req;
    563 
    564   if (NULL == cls)
    565   {
    566     fprintf (stderr, "The 'cls' parameter is NULL "
    567              "at line %d.\n", (int) __LINE__);
    568     fflush (stderr);
    569     abort ();
    570   }
    571   if ((unsigned int) TEST_MAGIC_MARKER1 != param->magic1)
    572   {
    573     fprintf (stderr, "The 'param->magic1' has wrong value "
    574              "at line %d.\n", (int) __LINE__);
    575     fflush (stderr);
    576     abort ();
    577   }
    578   if (NULL == connection)
    579   {
    580     fprintf (stderr, "The 'connection' parameter is NULL "
    581              "at line %d.\n", (int) __LINE__);
    582     param->err_flag = 1;
    583     return MHD_NO; /* Should not reply */
    584   }
    585   if (1)
    586   { /* Simple check for 'connection' parameter validity */
    587     const union MHD_ConnectionInfo *conn_info;
    588     conn_info =
    589       MHD_get_connection_info (connection,
    590                                MHD_CONNECTION_INFO_CONNECTION_TIMEOUT);
    591     if (NULL == conn_info)
    592     {
    593       fprintf (stderr, "The 'MHD_get_connection_info' has returned NULL "
    594                "at line %d.\n", (int) __LINE__);
    595       param->err_flag = 1;
    596     }
    597     else if (expected_conn_timeout != conn_info->connection_timeout)
    598     {
    599       fprintf (stderr, "The 'MHD_get_connection_info' has returned "
    600                "unexpected timeout value "
    601                "at line %d.\n", (int) __LINE__);
    602       param->err_flag = 1;
    603     }
    604   }
    605   if (NULL == url)
    606   {
    607     fprintf (stderr, "The 'url' parameter is NULL "
    608              "at line %d.\n", (int) __LINE__);
    609     param->err_flag = 1;
    610   }
    611   if (NULL == method)
    612   {
    613     fprintf (stderr, "The 'method' parameter is NULL "
    614              "at line %d.\n", (int) __LINE__);
    615     param->err_flag = 1;
    616     return MHD_NO; /* Should not reply */
    617   }
    618   if (NULL == version)
    619   {
    620     fprintf (stderr, "The 'version' parameter is NULL "
    621              "at line %d.\n", (int) __LINE__);
    622     param->err_flag = 1;
    623     return MHD_NO; /* Should not reply */
    624   }
    625   if (NULL == upload_data_size)
    626   {
    627     fprintf (stderr, "The 'upload_data_size' parameter is NULL "
    628              "at line %d.\n", (int) __LINE__);
    629     param->err_flag = 1;
    630     return MHD_NO; /* Should not reply */
    631   }
    632   if ((0 != *upload_data_size) && (NULL == upload_data))
    633   {
    634     fprintf (stderr, "The 'upload_data' parameter is NULL "
    635              "while '*upload_data_size' is not zero "
    636              "at line %d.\n", (int) __LINE__);
    637     param->err_flag = 1;
    638     return MHD_NO; /* Should not reply */
    639   }
    640   if ((NULL != upload_data) && (0 == *upload_data_size))
    641   {
    642     fprintf (stderr, "The 'upload_data' parameter is NOT NULL "
    643              "while '*upload_data_size' is zero "
    644              "at line %d.\n", (int) __LINE__);
    645     param->err_flag = 1;
    646     return MHD_NO; /* Should not reply */
    647   }
    648 
    649   if (0 != param->num_replies)
    650   {
    651     /* Phantom "second" request due to the fuzzing of the input. Refuse. */
    652     return MHD_NO;
    653   }
    654 
    655   is_post_req = (0 == strcmp (method, MHD_HTTP_METHOD_POST));
    656   if ((0 != strcmp (method, MHD_HTTP_METHOD_GET))
    657       && (0 != strcmp (method, MHD_HTTP_METHOD_HEAD))
    658       && (0 != strcmp (method, MHD_HTTP_METHOD_PUT))
    659       && (! is_post_req))
    660   {
    661     /* Unsupported method for this callback */
    662     return send_error_response (connection, param, MHD_HTTP_NOT_IMPLEMENTED,
    663                                 METHOD_NOT_SUPPORTED,
    664                                 MHD_STATICSTR_LEN_ (METHOD_NOT_SUPPORTED));
    665   }
    666 
    667   if (NULL == *req_cls)
    668   {
    669     if (! is_post_req)
    670     { /* Use static memory */
    671       *req_cls = &static_req_pr_data;
    672     }
    673     else
    674     { /* POST request, use PostProcessor */
    675       req_pr_data =
    676         (struct req_process_strct *) malloc (sizeof (struct req_process_strct));
    677       if (NULL == req_pr_data)
    678       {
    679         fprintf (stderr, "malloc() failed "
    680                  "at line %d.\n", (int) __LINE__);
    681         return MHD_NO;
    682       }
    683       req_pr_data->magic2 = TEST_MAGIC_MARKER2;
    684       req_pr_data->is_static = 0;
    685       req_pr_data->post_data_sum = 0;
    686       req_pr_data->postprocsr = MHD_create_post_processor (connection, 1024,
    687                                                            &post_iterator,
    688                                                            req_pr_data);
    689       if (NULL == req_pr_data->postprocsr)
    690       {
    691         free (req_pr_data);
    692         if (NULL == upload_data)
    693           return send_error_response (connection, param, MHD_HTTP_BAD_REQUEST,
    694                                       POST_DATA_BROKEN,
    695                                       MHD_STATICSTR_LEN_ (POST_DATA_BROKEN));
    696         else
    697           return MHD_NO; /* Cannot handle request, broken POST */
    698       }
    699       *req_cls = req_pr_data;
    700     }
    701     if (NULL == upload_data)
    702       return MHD_YES;
    703   }
    704   req_pr_data = (struct req_process_strct *) *req_cls;
    705 
    706   data_sum = 0;
    707   if (NULL != upload_data)
    708   {
    709     if (is_post_req)
    710     {
    711       if (MHD_YES != MHD_post_process (req_pr_data->postprocsr,
    712                                        upload_data, *upload_data_size))
    713       {
    714         free_req_pr_data (req_pr_data);
    715         *req_cls = NULL;
    716         /* Processing upload body (context), error reply cannot be queued here */
    717         return MHD_NO;
    718       }
    719       *upload_data_size = 0; /* All data have been processed */
    720     }
    721     else
    722     {
    723       /* Check that all 'upload_data' is addressable */
    724       size_t pos;
    725       for (pos = 0; pos < *upload_data_size; ++pos)
    726         data_sum =
    727           (unsigned char) (data_sum + (unsigned char) upload_data[pos]);
    728       if (0 != *upload_data_size)
    729       {
    730         if (3 >= *upload_data_size)
    731           *upload_data_size = 0;                             /* Consume all incoming data */
    732         else
    733           *upload_data_size = data_sum % *upload_data_size;  /* Pseudo-random */
    734       }
    735     }
    736     return MHD_YES;
    737   }
    738   if (is_post_req)
    739   {
    740     if (MHD_YES != MHD_destroy_post_processor (req_pr_data->postprocsr))
    741     {
    742       free (req_pr_data);
    743       *req_cls = NULL;
    744       return send_error_response (connection, param, MHD_HTTP_BAD_REQUEST,
    745                                   POST_DATA_BROKEN,
    746                                   MHD_STATICSTR_LEN_ (POST_DATA_BROKEN));
    747     }
    748     req_pr_data->postprocsr = NULL;
    749   }
    750   data_sum += (unsigned char) req_pr_data->post_data_sum;
    751   free_req_pr_data (req_pr_data);
    752   *req_cls = NULL;
    753 
    754   ret = MHD_YES;
    755   if (use_get_chunked)
    756   {
    757     struct content_cb_param_strct *cnt_cb_param;
    758     cnt_cb_param = malloc (sizeof (struct content_cb_param_strct));
    759     if (NULL == cnt_cb_param)
    760     {
    761       fprintf (stderr, "malloc() failed "
    762                "at line %d.\n", (int) __LINE__);
    763       /* External error, do not rise the error flag */
    764       return MHD_NO;
    765     }
    766     cnt_cb_param->magic0 = (unsigned int) TEST_MAGIC_MARKER0;
    767     response = MHD_create_response_from_callback (MHD_SIZE_UNKNOWN,
    768                                                   1024,
    769                                                   &content_cb, cnt_cb_param,
    770                                                   &crcf);
    771     if (NULL == response)
    772     {
    773       fprintf (stderr, "MHD_create_response_from_callback() failed "
    774                "at line %d.\n", (int) __LINE__);
    775       free (cnt_cb_param);
    776       param->err_flag = 1;
    777       ret = MHD_NO;
    778     }
    779     else
    780       cnt_cb_param->response = response;
    781   }
    782   else if (use_get || use_put || use_post)
    783   {
    784     /* Randomly choose the response page for the POST requests */
    785     if (0 == data_sum % 2)
    786       response =
    787         MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ (EMPTY_PAGE),
    788                                                 EMPTY_PAGE);
    789     else
    790       response =
    791         MHD_create_response_from_buffer_static (MHD_STATICSTR_LEN_ ( \
    792                                                   EMPTY_PAGE_ALT),
    793                                                 EMPTY_PAGE_ALT);
    794 
    795     if (NULL == response)
    796     {
    797       fprintf (stderr, "MHD_create_response_from_buffer_static() failed "
    798                "at line %d.\n", (int) __LINE__);
    799       param->err_flag = 1;
    800       ret = MHD_NO;
    801     }
    802   }
    803   else
    804   {
    805     fprintf (stderr, "Response is not implemented for this test. "
    806              "Internal logic is broken. "
    807              "At line %d.\n", (int) __LINE__);
    808     abort ();
    809   }
    810 
    811   if (NULL != response)
    812   {
    813     if ((MHD_YES == ret) &&
    814         (use_close || (! oneone && (0 != strcmp (version,
    815                                                  MHD_HTTP_VERSION_1_0)))))
    816     {
    817       ret = MHD_add_response_header (response,
    818                                      MHD_HTTP_HEADER_CONNECTION,
    819                                      "close");
    820       if (MHD_YES != ret)
    821       {
    822         fprintf (stderr, "MHD_add_response_header() failed "
    823                  "at line %d.\n", (int) __LINE__);
    824         param->err_flag = 1;
    825       }
    826     }
    827     if (MHD_YES == ret)
    828     {
    829       ret = MHD_queue_response (connection, MHD_HTTP_OK, response);
    830       if (MHD_YES != ret)
    831       {
    832         fprintf (stderr, "MHD_queue_response() failed "
    833                  "at line %d.\n", (int) __LINE__);
    834         param->err_flag = 1;
    835       }
    836     }
    837     else
    838       param->num_replies++;
    839 
    840     MHD_destroy_response (response);
    841   }
    842   else
    843   {
    844     fprintf (stderr, "MHD_create_response_from_buffer_static() failed "
    845              "at line %d.\n", (int) __LINE__);
    846     ret = MHD_NO;
    847   }
    848   return ret;
    849 }
    850 
    851 
    852 static void
    853 req_completed_cleanup (void *cls,
    854                        struct MHD_Connection *connection,
    855                        void **req_cls,
    856                        enum MHD_RequestTerminationCode toe)
    857 {
    858   struct ahc_param_strct *param = (struct ahc_param_strct *) cls;
    859   struct req_process_strct *req_pr_data = (struct req_process_strct *) *req_cls;
    860   (void) connection; /* Unused. Mute compiler warning. */
    861 
    862   if (NULL == param)
    863   {
    864     fprintf (stderr, "The 'cls' parameter is NULL at line %d.\n",
    865              (int) __LINE__);
    866     fflush (stderr);
    867     abort ();
    868   }
    869   if ((unsigned int) TEST_MAGIC_MARKER1 != param->magic1)
    870   {
    871     fprintf (stderr, "The 'param->magic1' has wrong value at line %d.\n",
    872              (int) __LINE__);
    873     fflush (stderr);
    874     abort ();
    875   }
    876   if (NULL == req_pr_data)
    877     return; /* The data have been freed */
    878   if ((unsigned int) TEST_MAGIC_MARKER2 != req_pr_data->magic2)
    879   {
    880     fprintf (stderr, "The 'req_pr_data->magic2' has wrong value at line %d.\n",
    881              (int) __LINE__);
    882     fflush (stderr);
    883     abort ();
    884   }
    885   if (MHD_REQUEST_TERMINATED_COMPLETED_OK == toe)
    886   {
    887     fprintf (stderr, "The request completed successful, but request cls has"
    888              "not been cleared. "
    889              "At line %d.\n", (int) __LINE__);
    890     param->err_flag = 1;
    891   }
    892   if (req_pr_data->is_static)
    893     return;
    894   if (NULL != req_pr_data->postprocsr)
    895     MHD_destroy_post_processor (req_pr_data->postprocsr);
    896   req_pr_data->postprocsr = NULL;
    897   free (req_pr_data);
    898   *req_cls = NULL;
    899 }
    900 
    901 
    902 /* Un-comment the next line (or use CPPFLAGS) to avoid
    903    logging of the traffic with debug builds */
    904 /* #define TEST_NO_PRINT_TRAFFIC 1 */
    905 
    906 #ifdef _DEBUG
    907 #ifdef TEST_PRINT_BODY
    908 #ifndef TEST_PRINT_BODY_RQ
    909 #define TEST_PRINT_BODY_RQ 1
    910 #endif /* TEST_PRINT_BODY_RQ */
    911 #ifndef TEST_PRINT_BODY_RP
    912 #define TEST_PRINT_BODY_RP 1
    913 #endif /* TEST_PRINT_BODY_RP */
    914 #endif /* TEST_PRINT_BODY */
    915 #endif /* _DEBUG */
    916 
    917 static int
    918 libcurl_debug_cb (CURL *handle,
    919                   curl_infotype type,
    920                   char *data,
    921                   size_t size,
    922                   void *ctx)
    923 {
    924   static const char excess_mark[] = "Excess found";
    925   static const size_t excess_mark_len = MHD_STATICSTR_LEN_ (excess_mark);
    926   struct CBC *cbc = ctx;
    927   (void) handle;
    928 
    929 #if defined(_DEBUG) && ! defined(TEST_NO_PRINT_TRAFFIC)
    930   switch (type)
    931   {
    932   case CURLINFO_TEXT:
    933     fprintf (stderr, "* %.*s", (int) size, data);
    934     break;
    935   case CURLINFO_HEADER_IN:
    936     fprintf (stderr, "< %.*s", (int) size, data);
    937     break;
    938   case CURLINFO_HEADER_OUT:
    939     fprintf (stderr, "> %.*s", (int) size, data);
    940     break;
    941   case CURLINFO_DATA_IN:
    942 #ifdef TEST_PRINT_BODY_RP
    943     fprintf (stderr, "<| %.*s\n", (int) size, data);
    944 #endif /* TEST_PRINT_BODY_RP */
    945     break;
    946   case CURLINFO_DATA_OUT:
    947 #ifdef TEST_PRINT_BODY_RQ
    948     fprintf (stderr, ">| %.*s\n", (int) size, data);
    949 #endif /* TEST_PRINT_BODY_RQ */
    950     break;
    951   case CURLINFO_SSL_DATA_IN:
    952   case CURLINFO_SSL_DATA_OUT:
    953   case CURLINFO_END:
    954   default:
    955     break;
    956   }
    957 #endif /* _DEBUG  && ! TEST_NO_PRINT_TRAFFIC */
    958   if (use_close || ! oneone)
    959   {
    960     /* Check for extra data only if every connection is terminated by MHD
    961        after one request, otherwise MHD may react on garbage after request
    962        data. */
    963     if (CURLINFO_TEXT == type)
    964     {
    965       if ((size >= excess_mark_len) &&
    966           (0 == memcmp (data, excess_mark, excess_mark_len)))
    967       {
    968         fprintf (stderr, "Extra data has been detected in MHD reply "
    969                  "at line %d.\n", (int) __LINE__);
    970         cbc->excess_found++;
    971       }
    972     }
    973   }
    974   return 0;
    975 }
    976 
    977 
    978 static CURL *
    979 setupCURL (struct CBC *cbc, uint16_t port
    980 #ifndef TEST_USE_STATIC_POST_DATA
    981            , curl_mime **mime
    982 #endif /* ! TEST_USE_STATIC_POST_DATA */
    983            )
    984 {
    985   CURL *c;
    986   CURLcode e;
    987   char *buf;
    988   const char *uri_to_use;
    989   const char *base_uri;
    990 
    991 #ifndef TEST_USE_STATIC_POST_DATA
    992   *mime = NULL;
    993 #endif /* ! TEST_USE_STATIC_POST_DATA */
    994 
    995   base_uri = run_with_socat ? TEST_BASE_URI_SOCAT : TEST_BASE_URI;
    996   if (! use_long_uri)
    997   {
    998     uri_to_use = base_uri;
    999     buf = NULL;
   1000   }
   1001   else
   1002   {
   1003     size_t pos;
   1004     size_t base_uri_len;
   1005 
   1006     base_uri_len = strlen (base_uri);
   1007     buf = malloc (TEST_STRING_VLONG_LEN + 1);
   1008     if (NULL == buf)
   1009     {
   1010       fprintf (stderr, "malloc() failed "
   1011                "at line %d.\n", (int) __LINE__);
   1012       return NULL;
   1013     }
   1014     memcpy (buf, base_uri, base_uri_len);
   1015     for (pos = base_uri_len;
   1016          pos < TEST_STRING_VLONG_LEN;
   1017          ++pos)
   1018     {
   1019       if (0 == pos % 9)
   1020         buf[pos] = '/';
   1021       else
   1022         buf[pos] = 'a' + (char) (unsigned char) (pos % ((unsigned char)
   1023                                                         ('z' - 'a' + 1)));
   1024     }
   1025     buf[TEST_STRING_VLONG_LEN] = 0;
   1026     uri_to_use = buf;
   1027   }
   1028   if (run_with_socat)
   1029     port = SOCAT_PORT;
   1030 
   1031   c = curl_easy_init ();
   1032   if (NULL == c)
   1033   {
   1034     fprintf (stderr, "curl_easy_init() failed "
   1035              "at line %d.\n", (int) __LINE__);
   1036     return NULL;
   1037   }
   1038 
   1039   if ((CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_URL,
   1040                                           uri_to_use))) &&
   1041       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L))) &&
   1042       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_WRITEFUNCTION,
   1043                                           &copyBuffer))) &&
   1044       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_WRITEDATA, cbc))) &&
   1045       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT,
   1046                                           ((long) CURL_TIMEOUT)))) &&
   1047       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_TIMEOUT,
   1048                                           ((long) CURL_TIMEOUT)))) &&
   1049       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_FAILONERROR, 0L))) &&
   1050 #ifdef _DEBUG
   1051       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_VERBOSE, 1L))) &&
   1052 #endif /* _DEBUG */
   1053       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_DEBUGFUNCTION,
   1054                                           &libcurl_debug_cb))) &&
   1055       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_DEBUGDATA,
   1056                                           cbc))) &&
   1057 #if CURL_AT_LEAST_VERSION (7, 45, 0)
   1058       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_DEFAULT_PROTOCOL,
   1059                                           "http"))) &&
   1060 #endif /* CURL_AT_LEAST_VERSION (7, 45, 0) */
   1061 #if CURL_AT_LEAST_VERSION (7, 85, 0)
   1062       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_PROTOCOLS_STR,
   1063                                           "http"))) &&
   1064 #elif CURL_AT_LEAST_VERSION (7, 19, 4)
   1065       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_PROTOCOLS,
   1066                                           CURLPROTO_HTTP))) &&
   1067 #endif /* CURL_AT_LEAST_VERSION (7, 19, 4) */
   1068       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
   1069                                           oneone ?
   1070                                           CURL_HTTP_VERSION_1_1 :
   1071                                           CURL_HTTP_VERSION_1_0))) &&
   1072 #if CURL_AT_LEAST_VERSION (7, 24, 0)
   1073       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_INTERFACE,
   1074                                           "host!127.0.0.101"))) &&
   1075 #else  /* ! CURL_AT_LEAST_VERSION (7, 24, 0) */
   1076       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_INTERFACE,
   1077                                           "127.0.0.101"))) &&
   1078 #endif /* ! CURL_AT_LEAST_VERSION (7, 24, 0) */
   1079       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_PORT, ((long) port)))) &&
   1080       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_HTTPHEADER,
   1081                                           use_long_header ?
   1082                                           libcurl_long_header : NULL)))
   1083       )
   1084   {
   1085     if (NULL != buf)
   1086     {
   1087       free (buf);
   1088       buf = NULL;
   1089     }
   1090     if (use_put)
   1091     {
   1092       if ((CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_READFUNCTION,
   1093                                               &putBuffer))) &&
   1094           (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_READDATA, cbc))) &&
   1095           (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_UPLOAD, (long) 1))) &&
   1096           (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_INFILESIZE_LARGE,
   1097                                               use_put_chunked ?
   1098                                               ((curl_off_t) -1) :
   1099                                               ((curl_off_t) cbc->up_size)))))
   1100       {
   1101         return c; /* Success exit point for 'use_put' */
   1102       }
   1103       else
   1104         fprintf (stderr, "PUT-related curl_easy_setopt() failed at line %d, "
   1105                  "error: %s\n", (int) __LINE__,
   1106                  curl_easy_strerror (e));
   1107     }
   1108     else if (use_post)
   1109     {
   1110       if (! use_post_form)
   1111       {
   1112         if ((CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_POST, (long) 1))) &&
   1113             (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_POSTFIELDS,
   1114                                                 POST_URLENC_DATA))) &&
   1115             (CURLE_OK ==
   1116              (e = curl_easy_setopt (c, CURLOPT_POSTFIELDSIZE,
   1117                                     MHD_STATICSTR_LEN_ (POST_URLENC_DATA)))))
   1118         {
   1119           return c; /* Success exit point for 'use_post' */
   1120         }
   1121         else
   1122           fprintf (stderr,
   1123                    "POST-related curl_easy_setopt() failed at line %d, "
   1124                    "error: %s\n", (int) __LINE__,
   1125                    curl_easy_strerror (e));
   1126       }
   1127       else
   1128       {
   1129 #ifndef TEST_USE_STATIC_POST_DATA
   1130         *mime = curl_mime_init (c);
   1131         if (NULL != *mime)
   1132         {
   1133           curl_mimepart *part;
   1134           if ((NULL != (part = curl_mime_addpart (*mime))) &&
   1135               (CURLE_OK == curl_mime_name (part, POST_KEY1)) &&
   1136               (CURLE_OK == curl_mime_data (part, POST_VALUE1,
   1137                                            MHD_STATICSTR_LEN_ (POST_VALUE1))) &&
   1138               (NULL != (part = curl_mime_addpart (*mime))) &&
   1139               (CURLE_OK == curl_mime_name (part, POST_KEY2)) &&
   1140               (CURLE_OK == curl_mime_data (part, POST_VALUE2,
   1141                                            MHD_STATICSTR_LEN_ (POST_VALUE2))))
   1142           {
   1143             if (CURLE_OK ==
   1144                 (e = curl_easy_setopt (c, CURLOPT_MIMEPOST, *mime)))
   1145               return c; /* Success exit point for 'use_post' */
   1146             else
   1147               fprintf (stderr, "curl_easy_setopt(c, CURLOPT_MIMEPOST, mime) "
   1148                        "failed at line %d, error: %s\n",
   1149                        (int) __LINE__, curl_easy_strerror (e));
   1150           }
   1151           else
   1152             fprintf (stderr, "curl_mime_addpart(), curl_mime_name() or "
   1153                      "curl_mime_data() failed.\n");
   1154         }
   1155         else
   1156           fprintf (stderr, "curl_mime_init() failed.\n");
   1157 
   1158 #else  /* TEST_USE_STATIC_POST_DATA */
   1159         if (CURLE_OK == (e = curl_easy_setopt (c,
   1160                                                CURLOPT_HTTPPOST, post_first)))
   1161         {
   1162           return c; /* Success exit point for 'use_post' */
   1163         }
   1164         else
   1165           fprintf (stderr, "POST form-related curl_easy_setopt() failed, "
   1166                    "error: %s\n", curl_easy_strerror (e));
   1167 #endif /* TEST_USE_STATIC_POST_DATA */
   1168       }
   1169     }
   1170     else
   1171       return c; /* Success exit point */
   1172   }
   1173   else
   1174     fprintf (stderr, "curl_easy_setopt() failed at line %d, "
   1175              "error: %s\n", (int) __LINE__,
   1176              curl_easy_strerror (e));
   1177 
   1178   curl_easy_cleanup (c);
   1179 #ifndef TEST_USE_STATIC_POST_DATA
   1180   if (NULL != *mime)
   1181     curl_mime_free (*mime);
   1182 #endif /* ! TEST_USE_STATIC_POST_DATA */
   1183 
   1184   if (NULL != buf)
   1185     free (buf);
   1186 
   1187   return NULL; /* Failure exit point */
   1188 }
   1189 
   1190 
   1191 static struct MHD_Daemon *
   1192 start_daemon_for_test (unsigned int daemon_flags, uint16_t *pport,
   1193                        struct ahc_param_strct *callback_param)
   1194 {
   1195   struct MHD_Daemon *d;
   1196   struct MHD_OptionItem ops[] = {
   1197     { MHD_OPTION_END, 0, NULL },
   1198     { MHD_OPTION_END, 0, NULL },
   1199     { MHD_OPTION_END, 0, NULL },
   1200     { MHD_OPTION_END, 0, NULL },
   1201     { MHD_OPTION_END, 0, NULL },
   1202     { MHD_OPTION_END, 0, NULL }
   1203   };
   1204   size_t num_opt;
   1205   size_t mem_limit;
   1206 
   1207   num_opt = 0;
   1208 
   1209   callback_param->magic1 = (unsigned int) TEST_MAGIC_MARKER1;
   1210   callback_param->err_flag = 0;
   1211   callback_param->num_replies = 0;
   1212 
   1213   /* The tests with intentionally huge URIs, headers or bodies need a memory
   1214      limit that matches the data they send; for all other tests the limit
   1215      comes from the option profile (which may be "keep the default"). */
   1216   if (use_put_large)
   1217     mem_limit = (size_t) (PUT_LARGE_SIZE / 4);
   1218   else if (use_long_header || use_long_uri)
   1219     mem_limit = (size_t) (TEST_STRING_VLONG_LEN / 2);
   1220   else
   1221     mem_limit = cur_profile->mem_limit;
   1222   if (0 != mem_limit)
   1223   {
   1224     ops[num_opt].option = MHD_OPTION_CONNECTION_MEMORY_LIMIT;
   1225     ops[num_opt].value = (intptr_t) mem_limit;
   1226     ++num_opt;
   1227   }
   1228   if (0 != cur_profile->conn_limit)
   1229   {
   1230     ops[num_opt].option = MHD_OPTION_CONNECTION_LIMIT;
   1231     ops[num_opt].value = (intptr_t) cur_profile->conn_limit;
   1232     ++num_opt;
   1233   }
   1234   if (0 != cur_profile->discipline_lvl)
   1235   {
   1236     ops[num_opt].option = cur_profile->use_legacy_strict ?
   1237                           MHD_OPTION_STRICT_FOR_CLIENT :
   1238                           MHD_OPTION_CLIENT_DISCIPLINE_LVL;
   1239     ops[num_opt].value = (intptr_t) cur_profile->discipline_lvl;
   1240     ++num_opt;
   1241   }
   1242   if (0 == (MHD_USE_INTERNAL_POLLING_THREAD & daemon_flags))
   1243   {
   1244     ops[num_opt].option = MHD_OPTION_APP_FD_SETSIZE;
   1245     ops[num_opt].value = (intptr_t) (FD_SETSIZE);
   1246     ++num_opt;
   1247   }
   1248   expected_conn_timeout = cur_profile->timeout;
   1249   d = MHD_start_daemon (daemon_flags /* | MHD_USE_ERROR_LOG */,
   1250                         *pport, NULL, NULL,
   1251                         &ahc_check, callback_param,
   1252                         MHD_OPTION_CONNECTION_TIMEOUT,
   1253                         expected_conn_timeout,
   1254                         MHD_OPTION_NOTIFY_COMPLETED,
   1255                         &req_completed_cleanup, callback_param,
   1256                         MHD_OPTION_ARRAY, ops,
   1257                         MHD_OPTION_END);
   1258   if (NULL == d)
   1259   {
   1260     fprintf (stderr, "MHD_start_daemon() failed "
   1261              "at line %d.\n", (int) __LINE__);
   1262     return NULL;
   1263   }
   1264 
   1265   /* Do not use accept4() as only accept() is intercepted by zzuf */
   1266   if (! run_with_socat)
   1267     MHD_avoid_accept4_ (d);
   1268 
   1269   if (0 == *pport)
   1270   {
   1271     const union MHD_DaemonInfo *dinfo;
   1272 
   1273     dinfo = MHD_get_daemon_info (d,
   1274                                  MHD_DAEMON_INFO_BIND_PORT);
   1275     if ( (NULL == dinfo) ||
   1276          (0 == dinfo->port) )
   1277     {
   1278       fprintf (stderr, "MHD_get_daemon_info() failed "
   1279                "at line %d.\n", (int) __LINE__);
   1280       MHD_stop_daemon (d);
   1281       return NULL;
   1282     }
   1283     *pport = dinfo->port;
   1284   }
   1285   return d;
   1286 }
   1287 
   1288 
   1289 /**
   1290  * Select the daemon option profile for the next daemon and print
   1291  * a description of the test that is about to start.
   1292  *
   1293  * @param daemon_flags the flags of the daemon that is about to be started
   1294  */
   1295 static void
   1296 print_test_starting (unsigned int daemon_flags)
   1297 {
   1298   static unsigned int profile_num;
   1299 
   1300   /* Every started daemon uses the next profile of the matrix, so that a
   1301      single run of a "_hostile" program sweeps the whole option matrix. */
   1302   cur_profile = zzuf_opt_profile (use_hostile_opts ? profile_num++ : 0);
   1303   fflush (stderr);
   1304   if (0 != (MHD_USE_INTERNAL_POLLING_THREAD & daemon_flags))
   1305   {
   1306     if (0 != (MHD_USE_THREAD_PER_CONNECTION & daemon_flags))
   1307     {
   1308       if (0 != (MHD_USE_POLL & daemon_flags))
   1309         printf ("\nStarting test with internal polling by poll() and "
   1310                 "thread-per-connection.\n");
   1311       else
   1312         printf ("\nStarting test with internal polling by select() and "
   1313                 "thread-per-connection.\n");
   1314     }
   1315     else
   1316     {
   1317       if (0 != (MHD_USE_POLL & daemon_flags))
   1318         printf ("\nStarting test with internal polling by poll().\n");
   1319       else if (0 != (MHD_USE_EPOLL & daemon_flags))
   1320         printf ("\nStarting test with internal polling by 'epoll'.\n");
   1321       else
   1322         printf ("\nStarting test with internal polling by select().\n");
   1323     }
   1324   }
   1325   else
   1326   {
   1327     if (0 != (MHD_USE_EPOLL & daemon_flags))
   1328       printf ("\nStarting test with%s thread safety with external polling "
   1329               "and internal 'epoll'.\n",
   1330               ((0 != (MHD_USE_NO_THREAD_SAFETY & daemon_flags)) ? "out" : ""));
   1331     else
   1332       printf ("\nStarting test with%s thread safety with external polling.\n",
   1333               ((0 != (MHD_USE_NO_THREAD_SAFETY & daemon_flags)) ? "out" : ""));
   1334   }
   1335   if (use_hostile_opts)
   1336     printf ("Daemon options profile '%s' "
   1337             "(mem_limit=%lu, conn_limit=%u, timeout=%u, %s=%d).\n",
   1338             cur_profile->name,
   1339             (unsigned long) cur_profile->mem_limit,
   1340             cur_profile->conn_limit,
   1341             cur_profile->timeout,
   1342             cur_profile->use_legacy_strict ?
   1343             "strict_for_client" : "discipline_lvl",
   1344             cur_profile->discipline_lvl);
   1345   fflush (stdout);
   1346 }
   1347 
   1348 
   1349 static unsigned int
   1350 testInternalPolling (uint16_t *pport, unsigned int daemon_flags)
   1351 {
   1352   struct MHD_Daemon *d;
   1353   CURL *c;
   1354   char buf[2048];
   1355   struct CBC cbc;
   1356   struct ahc_param_strct callback_param;
   1357   unsigned int ret;
   1358 #ifndef TEST_USE_STATIC_POST_DATA
   1359   curl_mime *mime;
   1360 #endif /* ! TEST_USE_STATIC_POST_DATA */
   1361 
   1362   if (0 == (MHD_USE_INTERNAL_POLLING_THREAD & daemon_flags))
   1363   {
   1364     fprintf (stderr, "Wrong internal flags, the test is broken. "
   1365              "At line %d.\n", (int) __LINE__);
   1366     abort (); /* Wrong flags, error in code */
   1367   }
   1368 
   1369   print_test_starting (daemon_flags);
   1370   initCBC (&cbc, buf, sizeof(buf));
   1371   d = start_daemon_for_test (daemon_flags, pport, &callback_param);
   1372   if (d == NULL)
   1373     return 1;
   1374 
   1375   ret = 0;
   1376   c = setupCURL (&cbc, *pport
   1377 #ifndef TEST_USE_STATIC_POST_DATA
   1378                  , &mime
   1379 #endif /* ! TEST_USE_STATIC_POST_DATA */
   1380                  );
   1381   if (NULL != c)
   1382   {
   1383     int i;
   1384 
   1385     for (i = dry_run ? LOOP_COUNT : 0; i < LOOP_COUNT; i++)
   1386     {
   1387       fprintf (stderr, ".");
   1388       callback_param.num_replies = 0;
   1389       resetCBC (&cbc);
   1390       /* Run libcurl without checking the result */
   1391       curl_easy_perform (c);
   1392       fflush (stderr);
   1393     }
   1394     curl_easy_cleanup (c);
   1395 #ifndef TEST_USE_STATIC_POST_DATA
   1396     if (NULL != mime)
   1397       curl_mime_free (mime);
   1398 #endif /* ! TEST_USE_STATIC_POST_DATA */
   1399   }
   1400   else
   1401     ret = 99; /* Not an MHD error */
   1402 
   1403   if ((0 == ret) && callback_param.err_flag)
   1404   {
   1405     fprintf (stderr, "One or more errors have been detected by "
   1406              "access handler callback function. "
   1407              "At line %d.\n", (int) __LINE__);
   1408     ret = 1;
   1409   }
   1410   else if ((0 == ret) && cbc.excess_found)
   1411   {
   1412     fprintf (stderr, "The extra reply data have been detected one "
   1413              "or more times. "
   1414              "At line %d.\n", (int) __LINE__);
   1415     ret = 1;
   1416   }
   1417 
   1418   fprintf (stderr, "\n");
   1419   MHD_stop_daemon (d);
   1420   fflush (stderr);
   1421   return ret;
   1422 }
   1423 
   1424 
   1425 static unsigned int
   1426 testExternalPolling (uint16_t *pport, unsigned int daemon_flags)
   1427 {
   1428   struct MHD_Daemon *d;
   1429   CURLM *multi;
   1430   char buf[2048];
   1431   struct CBC cbc;
   1432   struct ahc_param_strct callback_param;
   1433   unsigned int ret;
   1434 #ifndef TEST_USE_STATIC_POST_DATA
   1435   curl_mime *mime;
   1436 #endif /* ! TEST_USE_STATIC_POST_DATA */
   1437 
   1438   if (0 != (MHD_USE_INTERNAL_POLLING_THREAD & daemon_flags))
   1439   {
   1440     fprintf (stderr, "Wrong internal flags, the test is broken. "
   1441              "At line %d.\n", (int) __LINE__);
   1442     abort (); /* Wrong flags, error in code */
   1443   }
   1444 
   1445   print_test_starting (daemon_flags);
   1446   initCBC (&cbc, buf, sizeof(buf));
   1447   d = start_daemon_for_test (daemon_flags, pport, &callback_param);
   1448   if (d == NULL)
   1449     return 1;
   1450 
   1451   ret = 0;
   1452   multi = curl_multi_init ();
   1453   if (multi == NULL)
   1454   {
   1455     fprintf (stderr, "curl_multi_init() failed "
   1456              "at line %d.\n", (int) __LINE__);
   1457     ret = 99; /* Not an MHD error */
   1458   }
   1459   else
   1460   {
   1461     CURL *c;
   1462     c = setupCURL (&cbc, *pport
   1463 #ifndef TEST_USE_STATIC_POST_DATA
   1464                    , &mime
   1465 #endif /* ! TEST_USE_STATIC_POST_DATA */
   1466                    );
   1467 
   1468     if (NULL == c)
   1469       ret = 99; /* Not an MHD error */
   1470     else
   1471     {
   1472       int i;
   1473 
   1474       for (i = dry_run ? LOOP_COUNT : 0;
   1475            (i < LOOP_COUNT) && (0 == ret); i++)
   1476       {
   1477         CURLMcode mret;
   1478 
   1479         /* The same 'multi' handle will be used in transfers so
   1480            connection will be reused.
   1481            The same 'easy' handle is added (and removed later) to (re-)start
   1482            the same transfer. */
   1483         mret = curl_multi_add_handle (multi, c);
   1484         if (CURLM_OK != mret)
   1485         {
   1486           fprintf (stderr, "curl_multi_add_handle() failed at %d, "
   1487                    "error: %s\n", (int) __LINE__,
   1488                    curl_multi_strerror (mret));
   1489           ret = 99; /* Not an MHD error */
   1490         }
   1491         else
   1492         {
   1493           time_t start;
   1494 
   1495           fprintf (stderr, ".");
   1496           callback_param.num_replies = 0;
   1497           resetCBC (&cbc);
   1498           start = time (NULL);
   1499           do
   1500           {
   1501             fd_set rs;
   1502             fd_set ws;
   1503             fd_set es;
   1504             int maxfd_curl;
   1505             MHD_socket maxfd_mhd;
   1506             int maxfd;
   1507             int running;
   1508             struct timeval tv;
   1509 
   1510             maxfd_curl = 0;
   1511             maxfd_mhd = MHD_INVALID_SOCKET;
   1512             FD_ZERO (&rs);
   1513             FD_ZERO (&ws);
   1514             FD_ZERO (&es);
   1515             curl_multi_perform (multi, &running);
   1516             if (0 == running)
   1517             {
   1518               int msgs_left;
   1519               do
   1520               {
   1521                 (void) curl_multi_info_read (multi, &msgs_left);
   1522               } while (0 != msgs_left);
   1523               break; /* The transfer has been finished */
   1524             }
   1525             mret = curl_multi_fdset (multi, &rs, &ws, &es, &maxfd_curl);
   1526             if (CURLM_OK != mret)
   1527             {
   1528               fprintf (stderr, "curl_multi_fdset() failed at line %d, "
   1529                        "error: %s\n", (int) __LINE__,
   1530                        curl_multi_strerror (mret));
   1531               ret = 99; /* Not an MHD error */
   1532               break;
   1533             }
   1534             if (MHD_YES != MHD_get_fdset (d, &rs, &ws, &es, &maxfd_mhd))
   1535             {
   1536               fprintf (stderr, "MHD_get_fdset() failed "
   1537                        "at line %d.\n", (int) __LINE__);
   1538               ret = 1;
   1539               break;
   1540             }
   1541 #ifndef MHD_WINSOCK_SOCKETS
   1542             if ((int) maxfd_mhd > maxfd_curl)
   1543               maxfd = (int) maxfd_mhd;
   1544             else
   1545 #endif /* ! MHD_WINSOCK_SOCKETS */
   1546             maxfd = maxfd_curl;
   1547             tv.tv_sec = 0;
   1548             tv.tv_usec = 100 * 1000;
   1549             if (0 == MHD_get_timeout64s (d))
   1550               tv.tv_usec = 0;
   1551             else
   1552             {
   1553               long curl_to = -1;
   1554               curl_multi_timeout (multi, &curl_to);
   1555               if (0 == curl_to)
   1556                 tv.tv_usec = 0;
   1557             }
   1558             if (-1 == select (maxfd + 1, &rs, &ws, &es, &tv))
   1559             {
   1560 #ifdef MHD_POSIX_SOCKETS
   1561               if (EINTR != errno)
   1562                 fprintf (stderr, "Unexpected select() error "
   1563                          "at line %d.\n", (int) __LINE__);
   1564 #else  /* ! MHD_POSIX_SOCKETS */
   1565               if ((WSAEINVAL != WSAGetLastError ()) ||
   1566                   (0 != rs.fd_count) || (0 != ws.fd_count) ||
   1567                   (0 != es.fd_count))
   1568                 fprintf (stderr, "Unexpected select() error "
   1569                          "at line %d.\n", (int) __LINE__);
   1570               Sleep ((unsigned long) tv.tv_usec / 1000);
   1571 #endif /* ! MHD_POSIX_SOCKETS */
   1572             }
   1573             MHD_run (d);
   1574           } while (time (NULL) - start <= MHD_TIMEOUT);
   1575           /* Remove 'easy' handle from 'multi' handle to
   1576            * restart the transfer or to finish. */
   1577           curl_multi_remove_handle (multi, c);
   1578         }
   1579       }
   1580       curl_easy_cleanup (c);
   1581     }
   1582     curl_multi_cleanup (multi);
   1583 #ifndef TEST_USE_STATIC_POST_DATA
   1584     if (NULL != mime)
   1585       curl_mime_free (mime);
   1586 #endif /* ! TEST_USE_STATIC_POST_DATA */
   1587   }
   1588 
   1589   if ((0 == ret) && callback_param.err_flag)
   1590   {
   1591     fprintf (stderr, "One or more errors have been detected by "
   1592              "access handler callback function. "
   1593              "At line %d.\n", (int) __LINE__);
   1594     ret = 1;
   1595   }
   1596   else if ((0 == ret) && cbc.excess_found)
   1597   {
   1598     fprintf (stderr, "The extra reply data have been detected one "
   1599              "or more times. "
   1600              "At line %d.\n", (int) __LINE__);
   1601     ret = 1;
   1602   }
   1603 
   1604   fprintf (stderr, "\n");
   1605   MHD_stop_daemon (d);
   1606   return 0;
   1607 }
   1608 
   1609 
   1610 static unsigned int
   1611 run_all_checks (void)
   1612 {
   1613   uint16_t port;
   1614   unsigned int testRes;
   1615   unsigned int ret = 0;
   1616 
   1617   if (! run_with_socat)
   1618   {
   1619     if (MHD_are_sanitizers_enabled_ ())
   1620     {
   1621       fprintf (stderr, "Direct run with zzuf does not work with sanitizers. "
   1622                "At line %d.\n", (int) __LINE__);
   1623       return 77;
   1624     }
   1625     if (! MHD_is_avoid_accept4_possible_ ())
   1626     {
   1627       fprintf (stderr,
   1628                "Non-debug build of MHD on this platform use accept4() function. "
   1629                "Direct run with zzuf is not possible. "
   1630                "At line %d.\n", (int) __LINE__);
   1631       return 77;
   1632     }
   1633     if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
   1634       port = 0;     /* Use system automatic assignment */
   1635     else
   1636     {
   1637       port = TEST_BASE_PORT;  /* Use predefined port, may break parallel testing of another MHD build */
   1638       if (oneone)
   1639         port += 100;
   1640       if (use_long_uri)
   1641         port += 30;
   1642       else if (use_long_header)
   1643         port += 35;
   1644       else if (use_get_chunked)
   1645         port += 0;
   1646       else if (use_get)
   1647         port += 5;
   1648       else if (use_post_form)
   1649         port += 10;
   1650       else if (use_post)
   1651         port += 15;
   1652       else if (use_put_large)
   1653         port += 20;
   1654       else if (use_put_chunked)
   1655         port += 25;
   1656       if (use_hostile_opts)
   1657         port += 40;
   1658     }
   1659   }
   1660   else
   1661     port = TEST_BASE_PORT;  /* Use predefined port, may break parallel testing of another MHD build */
   1662 
   1663   if (! dry_run && (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS)))
   1664   {
   1665     testRes = testInternalPolling (&port, MHD_USE_SELECT_INTERNALLY);
   1666     if ((77 == testRes) || (99 == testRes))
   1667       return testRes;
   1668     ret += testRes;
   1669     testRes = testInternalPolling (&port, MHD_USE_SELECT_INTERNALLY
   1670                                    | MHD_USE_THREAD_PER_CONNECTION);
   1671     if ((77 == testRes) || (99 == testRes))
   1672       return testRes;
   1673     ret += testRes;
   1674 
   1675     if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_POLL))
   1676     {
   1677       testRes = testInternalPolling (&port, MHD_USE_POLL_INTERNALLY);
   1678       if ((77 == testRes) || (99 == testRes))
   1679         return testRes;
   1680       ret += testRes;
   1681       testRes = testInternalPolling (&port, MHD_USE_POLL_INTERNALLY
   1682                                      | MHD_USE_THREAD_PER_CONNECTION);
   1683       if ((77 == testRes) || (99 == testRes))
   1684         return testRes;
   1685       ret += testRes;
   1686     }
   1687 
   1688     if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_EPOLL))
   1689     {
   1690       testRes = testInternalPolling (&port, MHD_USE_EPOLL_INTERNALLY);
   1691       if ((77 == testRes) || (99 == testRes))
   1692         return testRes;
   1693     }
   1694     testRes = testExternalPolling (&port, MHD_NO_FLAG);
   1695   }
   1696   testRes = testExternalPolling (&port, MHD_USE_NO_THREAD_SAFETY);
   1697   if ((77 == testRes) || (99 == testRes))
   1698     return testRes;
   1699   ret += testRes;
   1700 
   1701   return ret;
   1702 }
   1703 
   1704 
   1705 int
   1706 main (int argc, char *const *argv)
   1707 {
   1708   unsigned int res;
   1709   int use_magic_exit_codes;
   1710 
   1711   oneone = ! has_in_name (argv[0], "10");
   1712   use_get = has_in_name (argv[0], "_get");
   1713   use_get_chunked = has_in_name (argv[0], "_get_chunked");
   1714   use_put = has_in_name (argv[0], "_put");
   1715   use_put_large = has_in_name (argv[0], "_put_large");
   1716   use_put_chunked = has_in_name (argv[0], "_put_chunked");
   1717   use_post = has_in_name (argv[0], "_post");
   1718   use_post_form = has_in_name (argv[0], "_post_form");
   1719   use_long_header = has_in_name (argv[0], "_long_header");
   1720   use_long_uri = has_in_name (argv[0], "_long_uri");
   1721   use_close = has_in_name (argv[0], "_close");
   1722   use_hostile_opts = has_in_name (argv[0], "_hostile");
   1723   cur_profile = zzuf_opt_profile (0);
   1724 
   1725   run_with_socat = has_param (argc, argv, "--with-socat");
   1726   dry_run = has_param (argc, argv, "--dry-run") ||
   1727             has_param (argc, argv, "-n");
   1728 
   1729   if (1 !=
   1730       ((use_get ? 1 : 0) + (use_put ? 1 : 0) + (use_post ? 1 : 0)))
   1731   {
   1732     fprintf (stderr, "Wrong test name '%s': no or multiple indications "
   1733              "for the test type.\n", argv[0] ? argv[0] : "(NULL)");
   1734     return 99;
   1735   }
   1736   use_magic_exit_codes = run_with_socat || dry_run;
   1737 
   1738   /* zzuf cannot bypass exit values.
   1739      Unless 'dry run' is used, do not return errors for external error
   1740      conditions (like out-of-memory) as they will be reported as test failures. */
   1741   if (! test_global_init ())
   1742     return use_magic_exit_codes ? 99 : 0;
   1743   res = run_all_checks ();
   1744   test_global_deinit ();
   1745   if (99 == res)
   1746     return use_magic_exit_codes ? 99 : 0;
   1747   if (77 == res)
   1748     return use_magic_exit_codes ? 77 : 0;
   1749   return (0 == res) ? 0 : 1;       /* 0 == pass */
   1750 }