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 ©Buffer))) && 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 }