libmicrohttpd

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

mhd_zzuf_common.c (28164B)


      1 /*
      2      This file is part of GNU libmicrohttpd
      3      Copyright (C) 2026 Christian Grothoff
      4 
      5      GNU libmicrohttpd is free software; you can redistribute it and/or
      6      modify it under the terms of the GNU Lesser General Public
      7      License as published by the Free Software Foundation; either
      8      version 2.1 of the License, or (at your option) any later version.
      9 
     10      This library is distributed in the hope that it will be useful,
     11      but WITHOUT ANY WARRANTY; without even the implied warranty of
     12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13      Lesser General Public License for more details.
     14 
     15      You should have received a copy of the GNU Lesser General Public
     16      License along with GNU libmicrohttpd.
     17      If not, see <http://www.gnu.org/licenses/>.
     18 */
     19 
     20 /**
     21  * @file testzzuf/mhd_zzuf_common.c
     22  * @brief  Shared support code for the fuzzing tests in this directory
     23  * @author Christian Grothoff
     24  */
     25 
     26 #include "platform.h"
     27 #include <curl/curl.h>
     28 #include <microhttpd.h>
     29 #include <stdlib.h>
     30 #include <string.h>
     31 #include <time.h>
     32 #include <errno.h>
     33 
     34 #ifdef HAVE_NETINET_IN_H
     35 #include <netinet/in.h>
     36 #endif /* HAVE_NETINET_IN_H */
     37 #ifdef HAVE_ARPA_INET_H
     38 #include <arpa/inet.h>
     39 #endif /* HAVE_ARPA_INET_H */
     40 #ifdef HAVE_NETINET_TCP_H
     41 #include <netinet/tcp.h>
     42 #endif /* HAVE_NETINET_TCP_H */
     43 
     44 #include "mhd_zzuf_common.h"
     45 #include "mhd_debug_funcs.h"
     46 #include "test_helpers.h"
     47 
     48 
     49 int zzuf_run_with_socat;
     50 int zzuf_dry_run;
     51 int zzuf_oneone;
     52 int zzuf_use_close;
     53 
     54 const struct zzuf_opt_profile *zzuf_active_profile;
     55 
     56 /**
     57  * The name of the running program, as given in argv[0].
     58  */
     59 static const char *zzuf_prog_name = "";
     60 
     61 
     62 /* ***  Platform abstraction for the raw socket client  *** */
     63 
     64 #if defined(MHD_POSIX_SOCKETS)
     65 #define ZZUF_CLOSE_SOCKET(s) close (s)
     66 #define ZZUF_SOCK_ERR (errno)
     67 #define ZZUF_ERR_IS_AGAIN(e) \
     68         ((EAGAIN == (e)) || (EWOULDBLOCK == (e)) || (EINTR == (e)))
     69 #define ZZUF_ERR_IS_INPROGRESS(e) \
     70         ((EINPROGRESS == (e)) || (EALREADY == (e)) || ZZUF_ERR_IS_AGAIN (e))
     71 #else  /* ! MHD_POSIX_SOCKETS */
     72 #define ZZUF_CLOSE_SOCKET(s) closesocket (s)
     73 #define ZZUF_SOCK_ERR ((int) WSAGetLastError ())
     74 #define ZZUF_ERR_IS_AGAIN(e) (WSAEWOULDBLOCK == (e))
     75 #define ZZUF_ERR_IS_INPROGRESS(e) \
     76         ((WSAEWOULDBLOCK == (e)) || (WSAEINPROGRESS == (e)) || \
     77          (WSAEALREADY == (e)))
     78 #endif /* ! MHD_POSIX_SOCKETS */
     79 
     80 #ifdef MSG_NOSIGNAL
     81 #define ZZUF_SEND_FLAGS MSG_NOSIGNAL
     82 #else  /* ! MSG_NOSIGNAL */
     83 #define ZZUF_SEND_FLAGS 0
     84 #endif /* ! MSG_NOSIGNAL */
     85 
     86 
     87 void
     88 zzuf_parse_common_args (int argc, char *const *argv)
     89 {
     90   const char *name;
     91 
     92   name = (NULL != argv[0]) ? argv[0] : "";
     93   zzuf_prog_name = name;
     94   zzuf_oneone = ! has_in_name (name, "10");
     95   zzuf_use_close = has_in_name (name, "_close");
     96   zzuf_run_with_socat = has_param (argc, argv, "--with-socat");
     97   zzuf_dry_run = has_param (argc, argv, "--dry-run") ||
     98                  has_param (argc, argv, "-n");
     99 #ifdef SIGPIPE
    100   /* The raw client writes to sockets that MHD may close at any moment
    101      because of the fuzzed input.  Do not die from SIGPIPE. */
    102   (void) signal (SIGPIPE, SIG_IGN);
    103 #endif /* SIGPIPE */
    104 }
    105 
    106 
    107 int
    108 zzuf_name_has (const char *marker)
    109 {
    110   return has_in_name (zzuf_prog_name, marker);
    111 }
    112 
    113 
    114 unsigned int
    115 zzuf_loop_count (void)
    116 {
    117   return zzuf_dry_run ? 0 : (unsigned int) ZZUF_LOOP_COUNT;
    118 }
    119 
    120 
    121 /* ***  The option matrix  *** */
    122 
    123 /**
    124  * The matrix of daemon options exercised by the fuzzing tests.
    125  *
    126  * Entry zero must stay "neutral": it reproduces the configuration that was
    127  * used before the option matrix was introduced.
    128  *
    129  * The memory limits below 1500 (MHD_BUF_INC_SIZE) are the interesting ones:
    130  * only with such a small connection pool MHD tries to re-use the tail of the
    131  * request header buffer ("shift back"), and only then the buffer arithmetic
    132  * around the last parsed header/argument element is reachable at all.
    133  *
    134  * #MHD_OPTION_SERVER_INSANITY is deliberately *not* used: in this MHD version
    135  * the only value of `enum MHD_DisableSanityCheck` is #MHD_DSC_SANE (zero), so
    136  * the option cannot disable anything and setting it would be a no-op.
    137  *
    138  */
    139 static const struct zzuf_opt_profile opt_profiles[] = {
    140   /* name             mem  conn timeout discipline legacy */
    141   { "plain",            0,   0, ZZUF_MHD_TIMEOUT,      0, 0 },
    142   { "pool-128",       128,   0, ZZUF_MHD_TIMEOUT,      0, 0 },
    143   { "pool-256-strict", 256,  0, ZZUF_MHD_TIMEOUT,      1, 0 },
    144   { "pool-512-lax",    512,  0, ZZUF_MHD_TIMEOUT,     -1, 0 },
    145   { "pool-1024-conn",  1024, 4, ZZUF_MHD_TIMEOUT,      2, 0 },
    146   { "pool-1400-hard",  1400, 2, 1,                     3, 0 },
    147   { "pool-768-loose",   768, 0, 1,                    -3, 0 },
    148   { "legacy-strict",    192, 0, ZZUF_MHD_TIMEOUT,      1, 1 },
    149   { "legacy-lax",       320, 0, ZZUF_MHD_TIMEOUT,     -1, 1 }
    150 };
    151 
    152 
    153 unsigned int
    154 zzuf_num_opt_profiles (void)
    155 {
    156   return (unsigned int) (sizeof(opt_profiles) / sizeof(opt_profiles[0]));
    157 }
    158 
    159 
    160 const struct zzuf_opt_profile *
    161 zzuf_opt_profile (unsigned int idx)
    162 {
    163   return opt_profiles + (idx % zzuf_num_opt_profiles ());
    164 }
    165 
    166 
    167 /* ***  Daemon handling  *** */
    168 
    169 uint16_t
    170 zzuf_pick_port (uint16_t offset)
    171 {
    172   if (zzuf_run_with_socat)
    173     return (uint16_t) ZZUF_BASE_PORT; /* socat forwards to this fixed port */
    174   if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
    175     return 0;                         /* Use system automatic assignment */
    176   /* Use a predefined port; may break parallel testing of another MHD build */
    177   return (uint16_t) (ZZUF_BASE_PORT + offset + (zzuf_oneone ? 100 : 0));
    178 }
    179 
    180 
    181 struct MHD_Daemon *
    182 zzuf_start_daemon (unsigned int daemon_flags,
    183                    uint16_t *pport,
    184                    const struct zzuf_opt_profile *prof,
    185                    size_t mem_limit_override,
    186                    MHD_AccessHandlerCallback ahc,
    187                    void *ahc_cls,
    188                    MHD_RequestCompletedCallback rcc,
    189                    void *rcc_cls,
    190                    const struct MHD_OptionItem *extra_opts)
    191 {
    192   struct MHD_Daemon *d;
    193   struct MHD_OptionItem ops[16];
    194   size_t num_opt;
    195   size_t mem_limit;
    196 
    197   num_opt = 0;
    198   mem_limit = (0 != mem_limit_override) ? mem_limit_override : prof->mem_limit;
    199   if (0 != mem_limit)
    200   {
    201     ops[num_opt].option = MHD_OPTION_CONNECTION_MEMORY_LIMIT;
    202     ops[num_opt].value = (intptr_t) mem_limit;
    203     ops[num_opt].ptr_value = NULL;
    204     ++num_opt;
    205   }
    206   if (0 != prof->conn_limit)
    207   {
    208     ops[num_opt].option = MHD_OPTION_CONNECTION_LIMIT;
    209     ops[num_opt].value = (intptr_t) prof->conn_limit;
    210     ops[num_opt].ptr_value = NULL;
    211     ++num_opt;
    212   }
    213   if (0 != prof->discipline_lvl)
    214   {
    215     ops[num_opt].option = prof->use_legacy_strict ?
    216                           MHD_OPTION_STRICT_FOR_CLIENT :
    217                           MHD_OPTION_CLIENT_DISCIPLINE_LVL;
    218     ops[num_opt].value = (intptr_t) prof->discipline_lvl;
    219     ops[num_opt].ptr_value = NULL;
    220     ++num_opt;
    221   }
    222   if (0 == (MHD_USE_INTERNAL_POLLING_THREAD & daemon_flags))
    223   {
    224     ops[num_opt].option = MHD_OPTION_APP_FD_SETSIZE;
    225     ops[num_opt].value = (intptr_t) (FD_SETSIZE);
    226     ops[num_opt].ptr_value = NULL;
    227     ++num_opt;
    228   }
    229   if (NULL != rcc)
    230   {
    231     ops[num_opt].option = MHD_OPTION_NOTIFY_COMPLETED;
    232     ops[num_opt].value = (intptr_t) rcc;
    233     ops[num_opt].ptr_value = rcc_cls;
    234     ++num_opt;
    235   }
    236   if (NULL != extra_opts)
    237   {
    238     size_t i;
    239 
    240     for (i = 0; MHD_OPTION_END != extra_opts[i].option; ++i)
    241     {
    242       if (num_opt + 2 > sizeof(ops) / sizeof(ops[0]))
    243       {
    244         fprintf (stderr, "Too many daemon options at line %d.\n",
    245                  (int) __LINE__);
    246         fflush (stderr);
    247         abort (); /* Broken test code */
    248       }
    249       ops[num_opt++] = extra_opts[i];
    250     }
    251   }
    252   if (num_opt + 1 > sizeof(ops) / sizeof(ops[0]))
    253   {
    254     fprintf (stderr, "Too many daemon options at line %d.\n", (int) __LINE__);
    255     fflush (stderr);
    256     abort (); /* Broken test code */
    257   }
    258   ops[num_opt].option = MHD_OPTION_END;
    259   ops[num_opt].value = 0;
    260   ops[num_opt].ptr_value = NULL;
    261   ++num_opt;
    262 
    263   d = MHD_start_daemon (daemon_flags /* | MHD_USE_ERROR_LOG */,
    264                         *pport, NULL, NULL,
    265                         ahc, ahc_cls,
    266                         MHD_OPTION_CONNECTION_TIMEOUT,
    267                         (unsigned int) prof->timeout,
    268                         MHD_OPTION_ARRAY, ops,
    269                         MHD_OPTION_END);
    270   if (NULL == d)
    271   {
    272     fprintf (stderr, "MHD_start_daemon() failed "
    273              "at line %d.\n", (int) __LINE__);
    274     return NULL;
    275   }
    276 
    277   /* Do not use accept4() as only accept() is intercepted by zzuf */
    278   if (! zzuf_run_with_socat)
    279     MHD_avoid_accept4_ (d);
    280 
    281   if (0 == *pport)
    282   {
    283     const union MHD_DaemonInfo *dinfo;
    284 
    285     dinfo = MHD_get_daemon_info (d, MHD_DAEMON_INFO_BIND_PORT);
    286     if ((NULL == dinfo) || (0 == dinfo->port))
    287     {
    288       fprintf (stderr, "MHD_get_daemon_info() failed "
    289                "at line %d.\n", (int) __LINE__);
    290       MHD_stop_daemon (d);
    291       return NULL;
    292     }
    293     *pport = dinfo->port;
    294   }
    295   return d;
    296 }
    297 
    298 
    299 /**
    300  * Print a description of the daemon that is about to be tested.
    301  *
    302  * @param daemon_flags the daemon flags
    303  * @param prof the option profile in use
    304  */
    305 static void
    306 print_test_starting (unsigned int daemon_flags,
    307                      const struct zzuf_opt_profile *prof)
    308 {
    309   const char *mode;
    310 
    311   fflush (stderr);
    312   if (0 != (MHD_USE_INTERNAL_POLLING_THREAD & daemon_flags))
    313   {
    314     if (0 != (MHD_USE_THREAD_PER_CONNECTION & daemon_flags))
    315       mode = (0 != (MHD_USE_POLL & daemon_flags)) ?
    316              "internal poll(), thread-per-connection" :
    317              "internal select(), thread-per-connection";
    318     else if (0 != (MHD_USE_POLL & daemon_flags))
    319       mode = "internal poll()";
    320     else if (0 != (MHD_USE_EPOLL & daemon_flags))
    321       mode = "internal 'epoll'";
    322     else
    323       mode = "internal select()";
    324   }
    325   else
    326   {
    327     if (0 != (MHD_USE_NO_THREAD_SAFETY & daemon_flags))
    328       mode = "external polling, no thread safety";
    329     else
    330       mode = "external polling";
    331   }
    332   printf ("\nStarting test with %s; options profile '%s' "
    333           "(mem_limit=%lu, conn_limit=%u, timeout=%u, %s=%d).\n",
    334           mode, prof->name,
    335           (unsigned long) prof->mem_limit,
    336           prof->conn_limit,
    337           prof->timeout,
    338           prof->use_legacy_strict ? "strict_for_client" : "discipline_lvl",
    339           prof->discipline_lvl);
    340   fflush (stdout);
    341 }
    342 
    343 
    344 /**
    345  * Run a single check: start a daemon, run the client, stop the daemon.
    346  *
    347  * @param[in,out] p the parameters
    348  * @param daemon_flags the flags for the daemon
    349  * @param[in,out] pprofile_num the number of the next profile to use
    350  * @return the result of the client callback, or 1 if the daemon could not
    351  *         be started
    352  */
    353 static unsigned int
    354 run_one_mode (struct zzuf_run_params *p,
    355               unsigned int daemon_flags,
    356               unsigned int *pprofile_num)
    357 {
    358   struct MHD_Daemon *d;
    359   const struct zzuf_opt_profile *prof;
    360   unsigned int ret;
    361 
    362   if (p->sweep_profiles)
    363     prof = zzuf_opt_profile ((*pprofile_num)++);
    364   else
    365     prof = zzuf_opt_profile (0);
    366   zzuf_active_profile = prof;
    367   print_test_starting (daemon_flags, prof);
    368   d = zzuf_start_daemon (daemon_flags, &p->port, prof, p->mem_limit_override,
    369                          p->ahc, p->ahc_cls, p->rcc, p->rcc_cls,
    370                          p->extra_opts);
    371   if (NULL == d)
    372     return 1;
    373   ret = p->client ((0 == (MHD_USE_INTERNAL_POLLING_THREAD & daemon_flags)) ?
    374                    d : NULL,
    375                    p->port,
    376                    p->client_cls);
    377   fprintf (stderr, "\n");
    378   MHD_stop_daemon (d);
    379   fflush (stderr);
    380   return ret;
    381 }
    382 
    383 
    384 unsigned int
    385 zzuf_check_runnable (void)
    386 {
    387   if (zzuf_run_with_socat)
    388     return 0;
    389   if (MHD_are_sanitizers_enabled_ ())
    390   {
    391     fprintf (stderr, "Direct run with zzuf does not work with sanitizers. "
    392              "At line %d.\n", (int) __LINE__);
    393     return 77;
    394   }
    395   if (! MHD_is_avoid_accept4_possible_ ())
    396   {
    397     fprintf (stderr,
    398              "Non-debug build of MHD on this platform use accept4() function. "
    399              "Direct run with zzuf is not possible. "
    400              "At line %d.\n", (int) __LINE__);
    401     return 77;
    402   }
    403   return 0;
    404 }
    405 
    406 
    407 unsigned int
    408 zzuf_run_polling_modes (struct zzuf_run_params *p)
    409 {
    410   unsigned int profile_num = p->profile_start;
    411   unsigned int testRes;
    412   unsigned int ret = 0;
    413 
    414   if (! zzuf_dry_run &&
    415       (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_THREADS)))
    416   {
    417     testRes = run_one_mode (p, MHD_USE_SELECT_INTERNALLY, &profile_num);
    418     if ((77 == testRes) || (99 == testRes))
    419       return testRes;
    420     ret += testRes;
    421     testRes = run_one_mode (p, MHD_USE_SELECT_INTERNALLY
    422                             | MHD_USE_THREAD_PER_CONNECTION, &profile_num);
    423     if ((77 == testRes) || (99 == testRes))
    424       return testRes;
    425     ret += testRes;
    426 
    427     if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_POLL))
    428     {
    429       testRes = run_one_mode (p, MHD_USE_POLL_INTERNALLY, &profile_num);
    430       if ((77 == testRes) || (99 == testRes))
    431         return testRes;
    432       ret += testRes;
    433       testRes = run_one_mode (p, MHD_USE_POLL_INTERNALLY
    434                               | MHD_USE_THREAD_PER_CONNECTION, &profile_num);
    435       if ((77 == testRes) || (99 == testRes))
    436         return testRes;
    437       ret += testRes;
    438     }
    439 
    440     if (MHD_YES == MHD_is_feature_supported (MHD_FEATURE_EPOLL))
    441     {
    442       testRes = run_one_mode (p, MHD_USE_EPOLL_INTERNALLY, &profile_num);
    443       if ((77 == testRes) || (99 == testRes))
    444         return testRes;
    445       ret += testRes;
    446     }
    447 
    448     testRes = run_one_mode (p, MHD_NO_FLAG, &profile_num);
    449     if ((77 == testRes) || (99 == testRes))
    450       return testRes;
    451     ret += testRes;
    452   }
    453   testRes = run_one_mode (p, MHD_USE_NO_THREAD_SAFETY, &profile_num);
    454   if ((77 == testRes) || (99 == testRes))
    455     return testRes;
    456   ret += testRes;
    457 
    458   return ret;
    459 }
    460 
    461 
    462 /* ***  The raw socket client  *** */
    463 
    464 /**
    465  * Switch the socket to the non-blocking mode.
    466  *
    467  * @param fd the socket to modify
    468  * @return non-zero on success, zero on failure
    469  */
    470 static int
    471 set_nonblocking (MHD_socket fd)
    472 {
    473 #if defined(MHD_POSIX_SOCKETS)
    474   int flags;
    475 
    476   flags = fcntl (fd, F_GETFL);
    477   if (-1 == flags)
    478     return 0;
    479   if (0 != (flags & O_NONBLOCK))
    480     return ! 0;
    481   return (-1 != fcntl (fd, F_SETFL, flags | O_NONBLOCK)) ? ! 0 : 0;
    482 #else  /* ! MHD_POSIX_SOCKETS */
    483   unsigned long mode = 1;
    484 
    485   return (0 == ioctlsocket (fd, FIONBIO, &mode)) ? ! 0 : 0;
    486 #endif /* ! MHD_POSIX_SOCKETS */
    487 }
    488 
    489 
    490 /**
    491  * Create the client socket and start connecting to the daemon.
    492  *
    493  * @param port the port MHD is listening on (ignored if socat is used)
    494  * @return the socket, or #MHD_INVALID_SOCKET on failure
    495  */
    496 static MHD_socket
    497 raw_connect (uint16_t port, int bypass_relay)
    498 {
    499   MHD_socket fd;
    500   struct sockaddr_in sa;
    501   const char *dst_ip;
    502   uint16_t dst_port;
    503   int on = 1;
    504 
    505   if (zzuf_run_with_socat && ! bypass_relay)
    506   {
    507     dst_ip = ZZUF_SOCAT_IP;
    508     dst_port = (uint16_t) ZZUF_SOCAT_PORT;
    509   }
    510   else
    511   {
    512     dst_ip = ZZUF_MHD_LISTEN_IP;
    513     dst_port = port;
    514   }
    515 
    516   fd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
    517   if (MHD_INVALID_SOCKET == fd)
    518     return MHD_INVALID_SOCKET;
    519   (void) setsockopt (fd, SOL_SOCKET, SO_REUSEADDR,
    520                      (const void *) &on, (socklen_t) sizeof(on));
    521   /* Use the same source address as libcurl does in the other tests, so that
    522      zzuf treats the traffic of this client exactly like libcurl's traffic. */
    523   memset (&sa, 0, sizeof(sa));
    524   sa.sin_family = AF_INET;
    525   sa.sin_port = 0;
    526   sa.sin_addr.s_addr = inet_addr (ZZUF_CLIENT_BIND_IP);
    527   /* Best effort only: some platforms do not have the whole 127.0.0.0/8
    528      range available. */
    529   (void) bind (fd, (const struct sockaddr *) &sa, (socklen_t) sizeof(sa));
    530 
    531   if (! set_nonblocking (fd))
    532   {
    533     (void) ZZUF_CLOSE_SOCKET (fd);
    534     return MHD_INVALID_SOCKET;
    535   }
    536 
    537   memset (&sa, 0, sizeof(sa));
    538   sa.sin_family = AF_INET;
    539   sa.sin_port = htons (dst_port);
    540   sa.sin_addr.s_addr = inet_addr (dst_ip);
    541   if (0 != connect (fd, (const struct sockaddr *) &sa, (socklen_t) sizeof(sa)))
    542   {
    543     const int err = ZZUF_SOCK_ERR;
    544     if (! ZZUF_ERR_IS_INPROGRESS (err))
    545     {
    546       (void) ZZUF_CLOSE_SOCKET (fd);
    547       return MHD_INVALID_SOCKET;
    548     }
    549   }
    550   return fd;
    551 }
    552 
    553 
    554 int
    555 zzuf_raw_exchange2 (struct MHD_Daemon *d_extern,
    556                     uint16_t port,
    557                     int bypass_relay,
    558                     const struct zzuf_raw_part *parts,
    559                     size_t num_parts,
    560                     unsigned int timeout_sec,
    561                     char *resp_buf,
    562                     size_t resp_buf_size,
    563                     size_t *resp_len)
    564 {
    565   MHD_socket fd;
    566   size_t cur_part = 0;
    567   size_t cur_off = 0;
    568   size_t resp_pos = 0;
    569   int send_done = 0;
    570   int peer_closed = 0;
    571   time_t start;
    572   char rbuf[1024];
    573 
    574   if (NULL != resp_len)
    575     *resp_len = 0;
    576   fd = raw_connect (port, bypass_relay);
    577   if (MHD_INVALID_SOCKET == fd)
    578     return ZZUF_RAW_SETUP_FAILED;
    579 
    580   if (0 == num_parts)
    581     send_done = 1;
    582 
    583   start = time (NULL);
    584   while (1)
    585   {
    586     fd_set rs;
    587     fd_set ws;
    588     fd_set es;
    589     struct timeval tv;
    590     int maxfd;
    591     MHD_socket maxfd_mhd = MHD_INVALID_SOCKET;
    592 
    593     if (send_done && peer_closed)
    594       break;
    595     if (((time_t) timeout_sec) < (time (NULL) - start))
    596       break; /* The peer did not close the connection in time */
    597 
    598     FD_ZERO (&rs);
    599     FD_ZERO (&ws);
    600     FD_ZERO (&es);
    601 #if defined(MHD_POSIX_SOCKETS)
    602     if (FD_SETSIZE <= (int) fd)
    603     {
    604       (void) ZZUF_CLOSE_SOCKET (fd);
    605       return ZZUF_RAW_SETUP_FAILED;
    606     }
    607 #endif /* MHD_POSIX_SOCKETS */
    608     if (! peer_closed)
    609       FD_SET (fd, &rs);
    610     if (! send_done)
    611       FD_SET (fd, &ws);
    612     maxfd = (int) fd;
    613     if (NULL != d_extern)
    614     {
    615       if (MHD_YES != MHD_get_fdset (d_extern, &rs, &ws, &es, &maxfd_mhd))
    616       {
    617         fprintf (stderr, "MHD_get_fdset() failed "
    618                  "at line %d.\n", (int) __LINE__);
    619         (void) ZZUF_CLOSE_SOCKET (fd);
    620         return ZZUF_RAW_SETUP_FAILED;
    621       }
    622 #ifndef MHD_WINSOCK_SOCKETS
    623       if ((int) maxfd_mhd > maxfd)
    624         maxfd = (int) maxfd_mhd;
    625 #endif /* ! MHD_WINSOCK_SOCKETS */
    626     }
    627     tv.tv_sec = 0;
    628     tv.tv_usec = 25 * 1000;
    629     if (-1 == select (maxfd + 1, &rs, &ws, &es, &tv))
    630     {
    631 #if defined(MHD_POSIX_SOCKETS)
    632       if (EINTR != errno)
    633         fprintf (stderr, "Unexpected select() error "
    634                  "at line %d.\n", (int) __LINE__);
    635 #endif /* MHD_POSIX_SOCKETS */
    636     }
    637     if (NULL != d_extern)
    638       MHD_run (d_extern);
    639 
    640     if (! send_done)
    641     {
    642       ssize_t res;
    643 
    644       res = (ssize_t) send (fd,
    645                             (const void *) (parts[cur_part].data + cur_off),
    646                             parts[cur_part].size - cur_off,
    647                             ZZUF_SEND_FLAGS);
    648       if (0 > res)
    649       {
    650         const int err = ZZUF_SOCK_ERR;
    651         if (! ZZUF_ERR_IS_AGAIN (err))
    652         {
    653           /* MHD (or socat) closed the connection: a perfectly normal
    654              outcome when the traffic is being fuzzed. */
    655           send_done = 1;
    656           peer_closed = 1;
    657         }
    658       }
    659       else
    660       {
    661         cur_off += (size_t) res;
    662         if (cur_off >= parts[cur_part].size)
    663         {
    664           cur_off = 0;
    665           if (++cur_part >= num_parts)
    666             send_done = 1;
    667         }
    668       }
    669     }
    670 
    671     if (! peer_closed)
    672     {
    673       ssize_t res;
    674 
    675       res = (ssize_t) recv (fd, rbuf, sizeof(rbuf), 0);
    676       if (0 == res)
    677         peer_closed = 1;
    678       else if (0 > res)
    679       {
    680         const int err = ZZUF_SOCK_ERR;
    681         if (! ZZUF_ERR_IS_AGAIN (err))
    682           peer_closed = 1;
    683       }
    684       else if ((NULL != resp_buf) && (resp_pos < resp_buf_size))
    685       {
    686         size_t to_copy = (size_t) res;
    687         if (to_copy > resp_buf_size - resp_pos)
    688           to_copy = resp_buf_size - resp_pos;
    689         memcpy (resp_buf + resp_pos, rbuf, to_copy);
    690         resp_pos += to_copy;
    691         if (NULL != resp_len)
    692           *resp_len = resp_pos;
    693       }
    694     }
    695   }
    696   (void) ZZUF_CLOSE_SOCKET (fd);
    697 
    698   if (NULL != d_extern)
    699   {
    700     /* Give MHD a chance to notice the closed connection and to clean up. */
    701     unsigned int i;
    702     for (i = 0; i < 10; ++i)
    703       MHD_run (d_extern);
    704   }
    705   return ZZUF_RAW_OK;
    706 }
    707 
    708 
    709 int
    710 zzuf_raw_exchange (struct MHD_Daemon *d_extern,
    711                    uint16_t port,
    712                    const struct zzuf_raw_part *parts,
    713                    size_t num_parts,
    714                    unsigned int timeout_sec)
    715 {
    716   return zzuf_raw_exchange2 (d_extern, port, 0, parts, num_parts, timeout_sec,
    717                              NULL, 0, NULL);
    718 }
    719 
    720 
    721 int
    722 zzuf_raw_request (struct MHD_Daemon *d_extern,
    723                   uint16_t port,
    724                   const char *request)
    725 {
    726   struct zzuf_raw_part part;
    727 
    728   part.data = request;
    729   part.size = strlen (request);
    730   return zzuf_raw_exchange (d_extern, port, &part, 1,
    731                             (unsigned int) ZZUF_CLIENT_TIMEOUT);
    732 }
    733 
    734 
    735 /* ***  libcurl helpers  *** */
    736 
    737 #ifndef CURL_VERSION_BITS
    738 #define CURL_VERSION_BITS(x,y,z) ((x) << 16 | (y) << 8 | (z))
    739 #endif /* ! CURL_VERSION_BITS */
    740 #ifndef CURL_AT_LEAST_VERSION
    741 #define CURL_AT_LEAST_VERSION(x,y,z) \
    742         (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS (x, y, z))
    743 #endif /* ! CURL_AT_LEAST_VERSION */
    744 
    745 #define ZZUF_CURL_HOST "http:/" "/" ZZUF_MHD_LISTEN_IP
    746 #define ZZUF_CURL_HOST_SOCAT "http:/" "/" ZZUF_SOCAT_IP
    747 
    748 
    749 /**
    750  * The libcurl write callback: discards the data.
    751  *
    752  * @param ptr the received data
    753  * @param size the size of the item
    754  * @param nmemb the number of items
    755  * @param ctx the sink
    756  * @return the number of processed bytes
    757  */
    758 static size_t
    759 curl_sink_cb (void *ptr, size_t size, size_t nmemb, void *ctx)
    760 {
    761   struct zzuf_curl_sink *sink = (struct zzuf_curl_sink *) ctx;
    762 
    763   (void) ptr; /* Unused. Mute compiler warning. */
    764   if (sink->dn_pos + size * nmemb > sizeof(sink->buf))
    765     return 0;   /* Overflow, abort the transfer */
    766   memcpy (sink->buf + sink->dn_pos, ptr, size * nmemb);
    767   sink->dn_pos += size * nmemb;
    768   return size * nmemb;
    769 }
    770 
    771 
    772 CURL *
    773 zzuf_curl_setup (uint16_t port,
    774                  const char *uri_tail,
    775                  struct zzuf_curl_sink *sink)
    776 {
    777   CURL *c;
    778   CURLcode e;
    779   char *uri;
    780   size_t host_len;
    781   size_t tail_len;
    782   const char *host;
    783 
    784   host = zzuf_run_with_socat ? ZZUF_CURL_HOST_SOCAT : ZZUF_CURL_HOST;
    785   if (zzuf_run_with_socat)
    786     port = (uint16_t) ZZUF_SOCAT_PORT;
    787   host_len = strlen (host);
    788   tail_len = strlen (uri_tail);
    789   uri = malloc (host_len + tail_len + 1);
    790   if (NULL == uri)
    791   {
    792     fprintf (stderr, "malloc() failed at line %d.\n", (int) __LINE__);
    793     return NULL;
    794   }
    795   memcpy (uri, host, host_len);
    796   memcpy (uri + host_len, uri_tail, tail_len + 1);
    797 
    798   sink->dn_pos = 0;
    799   c = curl_easy_init ();
    800   if (NULL == c)
    801   {
    802     fprintf (stderr, "curl_easy_init() failed at line %d.\n", (int) __LINE__);
    803     free (uri);
    804     return NULL;
    805   }
    806   if ((CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_URL, uri))) &&
    807       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L))) &&
    808       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_WRITEFUNCTION,
    809                                           &curl_sink_cb))) &&
    810       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_WRITEDATA, sink))) &&
    811       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT,
    812                                           ((long) ZZUF_CLIENT_TIMEOUT)))) &&
    813       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_TIMEOUT,
    814                                           ((long) ZZUF_CLIENT_TIMEOUT)))) &&
    815       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_FAILONERROR, 0L))) &&
    816 #if CURL_AT_LEAST_VERSION (7, 45, 0)
    817       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_DEFAULT_PROTOCOL,
    818                                           "http"))) &&
    819 #endif /* CURL_AT_LEAST_VERSION (7, 45, 0) */
    820 #if CURL_AT_LEAST_VERSION (7, 85, 0)
    821       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_PROTOCOLS_STR,
    822                                           "http"))) &&
    823 #elif CURL_AT_LEAST_VERSION (7, 19, 4)
    824       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_PROTOCOLS,
    825                                           CURLPROTO_HTTP))) &&
    826 #endif /* CURL_AT_LEAST_VERSION (7, 19, 4) */
    827       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_HTTP_VERSION,
    828                                           zzuf_oneone ?
    829                                           CURL_HTTP_VERSION_1_1 :
    830                                           CURL_HTTP_VERSION_1_0))) &&
    831 #if CURL_AT_LEAST_VERSION (7, 24, 0)
    832       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_INTERFACE,
    833                                           "host!" ZZUF_CLIENT_BIND_IP))) &&
    834 #else  /* ! CURL_AT_LEAST_VERSION (7, 24, 0) */
    835       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_INTERFACE,
    836                                           ZZUF_CLIENT_BIND_IP))) &&
    837 #endif /* ! CURL_AT_LEAST_VERSION (7, 24, 0) */
    838       (CURLE_OK == (e = curl_easy_setopt (c, CURLOPT_PORT, ((long) port)))))
    839   {
    840     free (uri);
    841     return c; /* Success exit point */
    842   }
    843   fprintf (stderr, "curl_easy_setopt() failed at line %d, error: %s\n",
    844            (int) __LINE__, curl_easy_strerror (e));
    845   curl_easy_cleanup (c);
    846   free (uri);
    847   return NULL; /* Failure exit point */
    848 }
    849 
    850 
    851 int
    852 zzuf_curl_driver_init (struct zzuf_curl_driver *drv,
    853                        struct MHD_Daemon *d_extern)
    854 {
    855   drv->d_extern = d_extern;
    856   drv->multi = NULL;
    857   if (NULL == d_extern)
    858     return ! 0;
    859   drv->multi = curl_multi_init ();
    860   if (NULL == drv->multi)
    861   {
    862     fprintf (stderr, "curl_multi_init() failed at line %d.\n", (int) __LINE__);
    863     return 0;
    864   }
    865   return ! 0;
    866 }
    867 
    868 
    869 void
    870 zzuf_curl_driver_deinit (struct zzuf_curl_driver *drv)
    871 {
    872   if (NULL != drv->multi)
    873     curl_multi_cleanup (drv->multi);
    874   drv->multi = NULL;
    875   drv->d_extern = NULL;
    876 }
    877 
    878 
    879 void
    880 zzuf_curl_driver_perform (struct zzuf_curl_driver *drv,
    881                           CURL *c)
    882 {
    883   CURLMcode mret;
    884   time_t start;
    885 
    886   if (NULL == drv->d_extern)
    887   {
    888     /* The daemon has an internal polling thread, just run the transfer. */
    889     (void) curl_easy_perform (c);
    890     return;
    891   }
    892   mret = curl_multi_add_handle (drv->multi, c);
    893   if (CURLM_OK != mret)
    894   {
    895     fprintf (stderr, "curl_multi_add_handle() failed at line %d, error: %s\n",
    896              (int) __LINE__, curl_multi_strerror (mret));
    897     return;
    898   }
    899   start = time (NULL);
    900   do
    901   {
    902     fd_set rs;
    903     fd_set ws;
    904     fd_set es;
    905     int maxfd_curl;
    906     MHD_socket maxfd_mhd;
    907     int maxfd;
    908     int running;
    909     struct timeval tv;
    910 
    911     maxfd_curl = 0;
    912     maxfd_mhd = MHD_INVALID_SOCKET;
    913     FD_ZERO (&rs);
    914     FD_ZERO (&ws);
    915     FD_ZERO (&es);
    916     curl_multi_perform (drv->multi, &running);
    917     if (0 == running)
    918     {
    919       int msgs_left;
    920       do
    921       {
    922         (void) curl_multi_info_read (drv->multi, &msgs_left);
    923       } while (0 != msgs_left);
    924       break; /* The transfer has been finished */
    925     }
    926     mret = curl_multi_fdset (drv->multi, &rs, &ws, &es, &maxfd_curl);
    927     if (CURLM_OK != mret)
    928     {
    929       fprintf (stderr, "curl_multi_fdset() failed at line %d, error: %s\n",
    930                (int) __LINE__, curl_multi_strerror (mret));
    931       break;
    932     }
    933     if (MHD_YES != MHD_get_fdset (drv->d_extern, &rs, &ws, &es, &maxfd_mhd))
    934     {
    935       fprintf (stderr, "MHD_get_fdset() failed at line %d.\n", (int) __LINE__);
    936       break;
    937     }
    938 #ifndef MHD_WINSOCK_SOCKETS
    939     if ((int) maxfd_mhd > maxfd_curl)
    940       maxfd = (int) maxfd_mhd;
    941     else
    942 #endif /* ! MHD_WINSOCK_SOCKETS */
    943     maxfd = maxfd_curl;
    944     tv.tv_sec = 0;
    945     tv.tv_usec = 25 * 1000;
    946     if (0 == MHD_get_timeout64s (drv->d_extern))
    947       tv.tv_usec = 0;
    948     else
    949     {
    950       long curl_to = -1;
    951       curl_multi_timeout (drv->multi, &curl_to);
    952       if (0 == curl_to)
    953         tv.tv_usec = 0;
    954     }
    955     if (-1 == select (maxfd + 1, &rs, &ws, &es, &tv))
    956     {
    957 #if defined(MHD_POSIX_SOCKETS)
    958       if (EINTR != errno)
    959         fprintf (stderr, "Unexpected select() error at line %d.\n",
    960                  (int) __LINE__);
    961 #else  /* ! MHD_POSIX_SOCKETS */
    962       if ((WSAEINVAL != WSAGetLastError ()) ||
    963           (0 != rs.fd_count) || (0 != ws.fd_count) || (0 != es.fd_count))
    964         fprintf (stderr, "Unexpected select() error at line %d.\n",
    965                  (int) __LINE__);
    966       Sleep ((unsigned long) tv.tv_usec / 1000);
    967 #endif /* ! MHD_POSIX_SOCKETS */
    968     }
    969     MHD_run (drv->d_extern);
    970   } while (time (NULL) - start <= (time_t) ZZUF_CLIENT_TIMEOUT);
    971   curl_multi_remove_handle (drv->multi, c);
    972 }