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 }