test_digestauth_concurrent.c (22553B)
1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2010 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 test_digestauth_concurrent.c 24 * @brief Testcase for libmicrohttpd concurrent Digest Authorisation 25 * @author Amr Ali 26 * @author Karlson2k (Evgeny Grin) 27 */ 28 29 #include "mhd_options.h" 30 #include "platform.h" 31 #include <curl/curl.h> 32 #include <microhttpd.h> 33 #include <stdlib.h> 34 #include <string.h> 35 #include <time.h> 36 #if defined(MHD_HTTPS_REQUIRE_GCRYPT) && \ 37 (defined(MHD_SHA256_TLSLIB) || defined(MHD_MD5_TLSLIB)) 38 #define NEED_GCRYP_INIT 1 39 #include <gcrypt.h> 40 #endif /* MHD_HTTPS_REQUIRE_GCRYPT && (MHD_SHA256_TLSLIB || MHD_MD5_TLSLIB) */ 41 #include <errno.h> 42 43 #ifndef WINDOWS 44 #include <sys/socket.h> 45 #include <unistd.h> 46 #else 47 #include <wincrypt.h> 48 #endif 49 50 #include <pthread.h> 51 52 #include "mhd_has_param.h" 53 54 /* Turn any MHD_PANIC() or failing mhd_assert() reached from this 55 test into a marked, classifiable test error (TESTING.md, P5). */ 56 #include "mhd_panic_tripwire.h" 57 58 #ifndef CURL_VERSION_BITS 59 #define CURL_VERSION_BITS(x,y,z) ((x) << 16 | (y) << 8 | (z)) 60 #endif /* ! CURL_VERSION_BITS */ 61 #ifndef CURL_AT_LEAST_VERSION 62 #define CURL_AT_LEAST_VERSION(x,y,z) \ 63 (LIBCURL_VERSION_NUM >= CURL_VERSION_BITS (x, y, z)) 64 #endif /* ! CURL_AT_LEAST_VERSION */ 65 66 #ifndef _MHD_INSTRMACRO 67 /* Quoted macro parameter */ 68 #define _MHD_INSTRMACRO(a) #a 69 #endif /* ! _MHD_INSTRMACRO */ 70 #ifndef _MHD_STRMACRO 71 /* Quoted expanded macro parameter */ 72 #define _MHD_STRMACRO(a) _MHD_INSTRMACRO (a) 73 #endif /* ! _MHD_STRMACRO */ 74 75 #if defined(HAVE___FUNC__) 76 #define externalErrorExit(ignore) \ 77 _externalErrorExit_func (NULL, __func__, __LINE__) 78 #define externalErrorExitDesc(errDesc) \ 79 _externalErrorExit_func (errDesc, __func__, __LINE__) 80 #define libcurlErrorExit(ignore) \ 81 _libcurlErrorExit_func (NULL, __func__, __LINE__) 82 #define libcurlErrorExitDesc(errDesc) \ 83 _libcurlErrorExit_func (errDesc, __func__, __LINE__) 84 #define mhdErrorExit(ignore) \ 85 _mhdErrorExit_func (NULL, __func__, __LINE__) 86 #define mhdErrorExitDesc(errDesc) \ 87 _mhdErrorExit_func (errDesc, __func__, __LINE__) 88 #define checkCURLE_OK(libcurlcall) \ 89 _checkCURLE_OK_func ((libcurlcall), _MHD_STRMACRO (libcurlcall), \ 90 __func__, __LINE__) 91 #elif defined(HAVE___FUNCTION__) 92 #define externalErrorExit(ignore) \ 93 _externalErrorExit_func (NULL, __FUNCTION__, __LINE__) 94 #define externalErrorExitDesc(errDesc) \ 95 _externalErrorExit_func (errDesc, __FUNCTION__, __LINE__) 96 #define libcurlErrorExit(ignore) \ 97 _libcurlErrorExit_func (NULL, __FUNCTION__, __LINE__) 98 #define libcurlErrorExitDesc(errDesc) \ 99 _libcurlErrorExit_func (errDesc, __FUNCTION__, __LINE__) 100 #define mhdErrorExit(ignore) \ 101 _mhdErrorExit_func (NULL, __FUNCTION__, __LINE__) 102 #define mhdErrorExitDesc(errDesc) \ 103 _mhdErrorExit_func (errDesc, __FUNCTION__, __LINE__) 104 #define checkCURLE_OK(libcurlcall) \ 105 _checkCURLE_OK_func ((libcurlcall), _MHD_STRMACRO (libcurlcall), \ 106 __FUNCTION__, __LINE__) 107 #else 108 #define externalErrorExit(ignore) _externalErrorExit_func (NULL, NULL, __LINE__) 109 #define externalErrorExitDesc(errDesc) \ 110 _externalErrorExit_func (errDesc, NULL, __LINE__) 111 #define libcurlErrorExit(ignore) _libcurlErrorExit_func (NULL, NULL, __LINE__) 112 #define libcurlErrorExitDesc(errDesc) \ 113 _libcurlErrorExit_func (errDesc, NULL, __LINE__) 114 #define mhdErrorExit(ignore) _mhdErrorExit_func (NULL, NULL, __LINE__) 115 #define mhdErrorExitDesc(errDesc) _mhdErrorExit_func (errDesc, NULL, __LINE__) 116 #define checkCURLE_OK(libcurlcall) \ 117 _checkCURLE_OK_func ((libcurlcall), _MHD_STRMACRO (libcurlcall), NULL, \ 118 __LINE__) 119 #endif 120 121 122 _MHD_NORETURN static void 123 _externalErrorExit_func (const char *errDesc, const char *funcName, int lineNum) 124 { 125 fflush (stdout); 126 if ((NULL != errDesc) && (0 != errDesc[0])) 127 fprintf (stderr, "%s", errDesc); 128 else 129 fprintf (stderr, "System or external library call failed"); 130 if ((NULL != funcName) && (0 != funcName[0])) 131 fprintf (stderr, " in %s", funcName); 132 if (0 < lineNum) 133 fprintf (stderr, " at line %d", lineNum); 134 135 fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno, 136 strerror (errno)); 137 #ifdef MHD_WINSOCK_SOCKETS 138 fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ()); 139 #endif /* MHD_WINSOCK_SOCKETS */ 140 fflush (stderr); 141 exit (99); 142 } 143 144 145 /* Not actually used in this test */ 146 static char libcurl_errbuf[CURL_ERROR_SIZE] = ""; 147 148 _MHD_NORETURN static void 149 _libcurlErrorExit_func (const char *errDesc, const char *funcName, int lineNum) 150 { 151 fflush (stdout); 152 if ((NULL != errDesc) && (0 != errDesc[0])) 153 fprintf (stderr, "%s", errDesc); 154 else 155 fprintf (stderr, "CURL library call failed"); 156 if ((NULL != funcName) && (0 != funcName[0])) 157 fprintf (stderr, " in %s", funcName); 158 if (0 < lineNum) 159 fprintf (stderr, " at line %d", lineNum); 160 161 fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno, 162 strerror (errno)); 163 #ifdef MHD_WINSOCK_SOCKETS 164 fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ()); 165 #endif /* MHD_WINSOCK_SOCKETS */ 166 if (0 != libcurl_errbuf[0]) 167 fprintf (stderr, "Last libcurl error description: %s\n", libcurl_errbuf); 168 169 fflush (stderr); 170 exit (99); 171 } 172 173 174 _MHD_NORETURN static void 175 _mhdErrorExit_func (const char *errDesc, const char *funcName, int lineNum) 176 { 177 fflush (stdout); 178 if ((NULL != errDesc) && (0 != errDesc[0])) 179 fprintf (stderr, "%s", errDesc); 180 else 181 fprintf (stderr, "MHD unexpected error"); 182 if ((NULL != funcName) && (0 != funcName[0])) 183 fprintf (stderr, " in %s", funcName); 184 if (0 < lineNum) 185 fprintf (stderr, " at line %d", lineNum); 186 187 fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno, 188 strerror (errno)); 189 #ifdef MHD_WINSOCK_SOCKETS 190 fprintf (stderr, "WSAGetLastError() value: %d\n", (int) WSAGetLastError ()); 191 #endif /* MHD_WINSOCK_SOCKETS */ 192 193 fflush (stderr); 194 exit (8); 195 } 196 197 198 #if 0 199 /* Function unused in this test */ 200 static void 201 _checkCURLE_OK_func (CURLcode code, const char *curlFunc, 202 const char *funcName, int lineNum) 203 { 204 if (CURLE_OK == code) 205 return; 206 207 fflush (stdout); 208 if ((NULL != curlFunc) && (0 != curlFunc[0])) 209 fprintf (stderr, "'%s' resulted in '%s'", curlFunc, 210 curl_easy_strerror (code)); 211 else 212 fprintf (stderr, "libcurl function call resulted in '%s'", 213 curl_easy_strerror (code)); 214 if ((NULL != funcName) && (0 != funcName[0])) 215 fprintf (stderr, " in %s", funcName); 216 if (0 < lineNum) 217 fprintf (stderr, " at line %d", lineNum); 218 219 fprintf (stderr, ".\nLast errno value: %d (%s)\n", (int) errno, 220 strerror (errno)); 221 if (0 != libcurl_errbuf[0]) 222 fprintf (stderr, "Last libcurl error description: %s\n", libcurl_errbuf); 223 224 fflush (stderr); 225 exit (9); 226 } 227 228 229 #endif 230 231 232 /* Could be increased to facilitate debugging */ 233 #define TIMEOUTS_VAL 5 234 235 #define MHD_URI_BASE_PATH "/bar%20foo?key=value" 236 237 #define PAGE \ 238 "<html><head><title>libmicrohttpd demo</title></head><body>Access granted</body></html>" 239 240 #define DENIED \ 241 "<html><head><title>libmicrohttpd demo</title></head><body>Access denied</body></html>" 242 243 #define MY_OPAQUE "11733b200778ce33060f31c9af70a870ba96ddd4" 244 245 struct CBC 246 { 247 char *buf; 248 size_t pos; 249 size_t size; 250 }; 251 252 static int verbose; 253 254 static size_t 255 copyBuffer (void *ptr, 256 size_t size, 257 size_t nmemb, 258 void *ctx) 259 { 260 struct CBC *cbc = ctx; 261 262 if (cbc->pos + size * nmemb > cbc->size) 263 mhdErrorExitDesc ("Wrong too large data"); /* overflow */ 264 memcpy (&cbc->buf[cbc->pos], ptr, size * nmemb); 265 cbc->pos += size * nmemb; 266 return size * nmemb; 267 } 268 269 270 static enum MHD_Result 271 ahc_echo (void *cls, 272 struct MHD_Connection *connection, 273 const char *url, 274 const char *method, 275 const char *version, 276 const char *upload_data, 277 size_t *upload_data_size, 278 void **req_cls) 279 { 280 struct MHD_Response *response; 281 char *username; 282 const char *password = "testpass"; 283 const char *realm = "test@example.com"; 284 enum MHD_Result ret; 285 enum MHD_DigestAuthResult ret_e; 286 static int already_called_marker; 287 (void) cls; (void) url; /* Unused. Silent compiler warning. */ 288 (void) method; (void) version; (void) upload_data; /* Unused. Silent compiler warning. */ 289 (void) upload_data_size; (void) req_cls; /* Unused. Silent compiler warning. */ 290 291 if (&already_called_marker != *req_cls) 292 { /* Called for the first time, request not fully read yet */ 293 *req_cls = &already_called_marker; 294 /* Wait for complete request */ 295 return MHD_YES; 296 } 297 298 username = MHD_digest_auth_get_username (connection); 299 if ( (username == NULL) || 300 (0 != strcmp (username, "testuser")) ) 301 { 302 response = MHD_create_response_from_buffer_static (strlen (DENIED), 303 DENIED); 304 if (NULL == response) 305 mhdErrorExitDesc ("MHD_create_response_from_buffer failed"); 306 ret = MHD_queue_auth_fail_response2 (connection, 307 realm, 308 MY_OPAQUE, 309 response, 310 MHD_NO, 311 MHD_DIGEST_ALG_MD5); 312 if (MHD_YES != ret) 313 mhdErrorExitDesc ("MHD_queue_auth_fail_response2 failed"); 314 MHD_destroy_response (response); 315 return ret; 316 } 317 ret_e = MHD_digest_auth_check3 (connection, 318 realm, 319 username, 320 password, 321 50 * TIMEOUTS_VAL, 322 0, MHD_DIGEST_AUTH_MULT_QOP_AUTH, 323 MHD_DIGEST_AUTH_MULT_ALGO3_MD5); 324 MHD_free (username); 325 if (ret_e != MHD_DAUTH_OK) 326 { 327 response = MHD_create_response_from_buffer_static (strlen (DENIED), 328 DENIED); 329 if (NULL == response) 330 mhdErrorExitDesc ("MHD_create_response_from_buffer() failed"); 331 ret = MHD_queue_auth_fail_response2 (connection, 332 realm, 333 MY_OPAQUE, 334 response, 335 (MHD_DAUTH_NONCE_STALE == ret_e) ? 336 MHD_YES : MHD_NO, 337 MHD_DIGEST_ALG_MD5); 338 if (MHD_YES != ret) 339 mhdErrorExitDesc ("MHD_queue_auth_fail_response2() failed"); 340 MHD_destroy_response (response); 341 return ret; 342 } 343 response = MHD_create_response_from_buffer_static (strlen (PAGE), 344 PAGE); 345 if (NULL == response) 346 mhdErrorExitDesc ("MHD_create_response_from_buffer() failed"); 347 ret = MHD_queue_response (connection, 348 MHD_HTTP_OK, 349 response); 350 if (MHD_YES != ret) 351 mhdErrorExitDesc ("MHD_queue_auth_fail_response2() failed"); 352 MHD_destroy_response (response); 353 return ret; 354 } 355 356 357 static CURL * 358 setupCURL (void *cbc, uint16_t port, char *errbuf) 359 { 360 CURL *c; 361 char url[512]; 362 363 if (1) 364 { 365 int res; 366 /* A workaround for some old libcurl versions, which ignore the specified 367 * port by CURLOPT_PORT when digest authorisation is used. */ 368 res = snprintf (url, (sizeof(url) / sizeof(url[0])), 369 "http://127.0.0.1:%u%s", (unsigned int) port, 370 MHD_URI_BASE_PATH); 371 if ((0 >= res) || ((sizeof(url) / sizeof(url[0])) <= (size_t) res)) 372 externalErrorExitDesc ("Cannot form request URL"); 373 } 374 375 c = curl_easy_init (); 376 if (NULL == c) 377 libcurlErrorExitDesc ("curl_easy_init() failed"); 378 379 if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_NOSIGNAL, 1L)) || 380 (CURLE_OK != curl_easy_setopt (c, CURLOPT_ERRORBUFFER, 381 errbuf)) || 382 (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEFUNCTION, 383 ©Buffer)) || 384 (CURLE_OK != curl_easy_setopt (c, CURLOPT_WRITEDATA, cbc)) || 385 (CURLE_OK != curl_easy_setopt (c, CURLOPT_CONNECTTIMEOUT, 386 ((long) TIMEOUTS_VAL))) || 387 (CURLE_OK != curl_easy_setopt (c, CURLOPT_TIMEOUT, 388 ((long) TIMEOUTS_VAL))) || 389 (CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTP_VERSION, 390 CURL_HTTP_VERSION_1_1)) || 391 (CURLE_OK != curl_easy_setopt (c, CURLOPT_FAILONERROR, 1L)) || 392 #ifdef _DEBUG 393 (CURLE_OK != curl_easy_setopt (c, CURLOPT_VERBOSE, 1L)) || 394 #endif /* _DEBUG */ 395 #if CURL_AT_LEAST_VERSION (7, 85, 0) 396 (CURLE_OK != curl_easy_setopt (c, CURLOPT_PROTOCOLS_STR, "http")) || 397 #elif CURL_AT_LEAST_VERSION (7, 19, 4) 398 (CURLE_OK != curl_easy_setopt (c, CURLOPT_PROTOCOLS, CURLPROTO_HTTP)) || 399 #endif /* CURL_AT_LEAST_VERSION (7, 19, 4) */ 400 #if CURL_AT_LEAST_VERSION (7, 45, 0) 401 (CURLE_OK != curl_easy_setopt (c, CURLOPT_DEFAULT_PROTOCOL, "http")) || 402 #endif /* CURL_AT_LEAST_VERSION (7, 45, 0) */ 403 (CURLE_OK != curl_easy_setopt (c, CURLOPT_PORT, ((long) port))) || 404 (CURLE_OK != curl_easy_setopt (c, CURLOPT_URL, url))) 405 libcurlErrorExitDesc ("curl_easy_setopt() failed"); 406 if ((CURLE_OK != curl_easy_setopt (c, CURLOPT_HTTPAUTH, CURLAUTH_DIGEST)) || 407 (CURLE_OK != curl_easy_setopt (c, CURLOPT_USERPWD, 408 "testuser:testpass"))) 409 libcurlErrorExitDesc ("curl_easy_setopt() authorization options failed"); 410 return c; 411 } 412 413 414 static void 415 getRnd (void *buf, size_t size) 416 { 417 #ifndef WINDOWS 418 int fd; 419 size_t off = 0; 420 421 fd = open ("/dev/urandom", 422 O_RDONLY); 423 if (-1 == fd) 424 externalErrorExitDesc ("Failed to open '/dev/urandom'"); 425 426 do 427 { 428 ssize_t res; 429 res = read (fd, ((uint8_t *) buf) + off, size - off); 430 if (0 > res) 431 externalErrorExitDesc ("Failed to read '/dev/urandom'"); 432 off += (size_t) res; 433 } while (off < size); 434 (void) close (fd); 435 #else 436 HCRYPTPROV cc; 437 BOOL b; 438 439 b = CryptAcquireContext (&cc, 440 NULL, 441 NULL, 442 PROV_RSA_FULL, 443 CRYPT_VERIFYCONTEXT | CRYPT_SILENT); 444 if (b == 0) 445 externalErrorExitDesc ("CryptAcquireContext() failed"); 446 b = CryptGenRandom (cc, (DWORD) size, (BYTE *) buf); 447 if (b == 0) 448 externalErrorExitDesc ("CryptGenRandom() failed"); 449 CryptReleaseContext (cc, 0); 450 #endif /* ! WINDOWS */ 451 } 452 453 454 struct curlWokerInfo 455 { 456 int workerNumber; 457 struct CBC cbc; 458 pthread_t tid; 459 /** 460 * The libcurl handle to run in thread 461 */ 462 CURL *c; 463 char *libcurl_errbuf; 464 /** 465 * Non-zero if worker is finished 466 */ 467 volatile int finished; 468 /** 469 * The number of successful worker results 470 */ 471 volatile unsigned int success; 472 }; 473 474 475 static void * 476 worker_func (void *param) 477 { 478 struct curlWokerInfo *const w = (struct curlWokerInfo *) param; 479 CURLcode req_result; 480 if (NULL == w) 481 externalErrorExit (); 482 483 req_result = curl_easy_perform (w->c); 484 if (CURLE_OK != req_result) 485 { 486 if (0 != w->libcurl_errbuf[0]) 487 fprintf (stderr, "Worker %d: first request failed. " 488 "libcurl error: '%s'.\n" 489 "libcurl error description: '%s'.\n", 490 w->workerNumber, curl_easy_strerror (req_result), 491 w->libcurl_errbuf); 492 else 493 fprintf (stderr, "Worker %d: first request failed. " 494 "libcurl error: '%s'.\n", 495 w->workerNumber, curl_easy_strerror (req_result)); 496 } 497 else 498 { 499 if (w->cbc.pos != strlen (PAGE)) 500 { 501 fprintf (stderr, "Worker %d: Got %u bytes ('%.*s'), expected %u bytes. ", 502 w->workerNumber, 503 (unsigned) w->cbc.pos, (int) w->cbc.pos, w->cbc.buf, 504 (unsigned) strlen (MHD_URI_BASE_PATH)); 505 mhdErrorExitDesc ("Wrong returned data length"); 506 } 507 if (0 != strncmp (PAGE, w->cbc.buf, strlen (PAGE))) 508 { 509 fprintf (stderr, "Worker %d: Got invalid response '%.*s'. ", 510 w->workerNumber, 511 (int) w->cbc.pos, w->cbc.buf); 512 mhdErrorExitDesc ("Wrong returned data"); 513 } 514 if (verbose) 515 printf ("Worker %d: first request successful.\n", w->workerNumber); 516 w->success++; 517 } 518 #ifdef _DEBUG 519 fflush (stderr); 520 fflush (stdout); 521 #endif /* _DEBUG */ 522 523 /* Second request */ 524 w->cbc.pos = 0; 525 req_result = curl_easy_perform (w->c); 526 if (CURLE_OK != req_result) 527 { 528 if (0 != w->libcurl_errbuf[0]) 529 fprintf (stderr, "Worker %d: second request failed. " 530 "libcurl error: '%s'.\n" 531 "libcurl error description: '%s'.\n", 532 w->workerNumber, curl_easy_strerror (req_result), 533 w->libcurl_errbuf); 534 else 535 fprintf (stderr, "Worker %d: second request failed. " 536 "libcurl error: '%s'.\n", 537 w->workerNumber, curl_easy_strerror (req_result)); 538 } 539 else 540 { 541 if (w->cbc.pos != strlen (PAGE)) 542 { 543 fprintf (stderr, "Worker %d: Got %u bytes ('%.*s'), expected %u bytes. ", 544 w->workerNumber, 545 (unsigned) w->cbc.pos, (int) w->cbc.pos, w->cbc.buf, 546 (unsigned) strlen (MHD_URI_BASE_PATH)); 547 mhdErrorExitDesc ("Wrong returned data length"); 548 } 549 if (0 != strncmp (PAGE, w->cbc.buf, strlen (PAGE))) 550 { 551 fprintf (stderr, "Worker %d: Got invalid response '%.*s'. ", 552 w->workerNumber, 553 (int) w->cbc.pos, w->cbc.buf); 554 mhdErrorExitDesc ("Wrong returned data"); 555 } 556 if (verbose) 557 printf ("Worker %d: second request successful.\n", w->workerNumber); 558 w->success++; 559 } 560 #ifdef _DEBUG 561 fflush (stderr); 562 fflush (stdout); 563 #endif /* _DEBUG */ 564 565 w->finished = ! 0; 566 return NULL; 567 } 568 569 570 #define CLIENT_BUF_SIZE 2048 571 572 static unsigned int 573 testDigestAuth (void) 574 { 575 struct MHD_Daemon *d; 576 char rnd[8]; 577 uint16_t port; 578 size_t i; 579 /* Run three workers in parallel so at least two workers would start within 580 * the same monotonic clock second.*/ 581 struct curlWokerInfo workers[3]; 582 unsigned int ret; 583 584 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 585 port = 0; 586 else 587 port = 4200; 588 589 getRnd (rnd, sizeof(rnd)); 590 591 d = MHD_start_daemon (MHD_USE_INTERNAL_POLLING_THREAD | MHD_USE_ERROR_LOG, 592 port, NULL, NULL, 593 &ahc_echo, NULL, 594 MHD_OPTION_DIGEST_AUTH_RANDOM, sizeof (rnd), rnd, 595 MHD_OPTION_NONCE_NC_SIZE, 300, 596 MHD_OPTION_THREAD_POOL_SIZE, 597 (unsigned int) (sizeof(workers) / sizeof(workers[0])), 598 MHD_OPTION_DIGEST_AUTH_DEFAULT_MAX_NC, (uint32_t) 999, 599 MHD_OPTION_END); 600 if (d == NULL) 601 return 1; 602 if (0 == port) 603 { 604 const union MHD_DaemonInfo *dinfo; 605 606 dinfo = MHD_get_daemon_info (d, 607 MHD_DAEMON_INFO_BIND_PORT); 608 if ( (NULL == dinfo) || 609 (0 == dinfo->port) ) 610 mhdErrorExitDesc ("MHD_get_daemon_info() failed"); 611 port = dinfo->port; 612 } 613 614 /* Initialise all workers */ 615 for (i = 0; i < sizeof(workers) / sizeof(workers[0]); i++) 616 { 617 struct curlWokerInfo *const w = workers + i; 618 w->workerNumber = (int) i + 1; /* Use 1-based numbering */ 619 w->cbc.buf = malloc (CLIENT_BUF_SIZE); 620 if (NULL == w->cbc.buf) 621 externalErrorExitDesc ("malloc() failed"); 622 w->cbc.size = CLIENT_BUF_SIZE; 623 w->cbc.pos = 0; 624 w->libcurl_errbuf = malloc (CURL_ERROR_SIZE); 625 if (NULL == w->libcurl_errbuf) 626 externalErrorExitDesc ("malloc() failed"); 627 w->libcurl_errbuf[0] = 0; 628 w->c = setupCURL (&w->cbc, port, w->libcurl_errbuf); 629 w->finished = 0; 630 w->success = 0; 631 } 632 633 /* Fire already initialised workers */ 634 for (i = 0; i < sizeof(workers) / sizeof(workers[0]); i++) 635 { 636 struct curlWokerInfo *const w = workers + i; 637 if (0 != pthread_create (&w->tid, NULL, &worker_func, w)) 638 externalErrorExitDesc ("pthread_create() failed"); 639 } 640 641 /* Collect results, cleanup workers */ 642 ret = 0; 643 for (i = 0; i < sizeof(workers) / sizeof(workers[0]); i++) 644 { 645 struct curlWokerInfo *const w = workers + i; 646 if (0 != pthread_join (w->tid, NULL)) 647 externalErrorExitDesc ("pthread_join() failed"); 648 curl_easy_cleanup (w->c); 649 free (w->libcurl_errbuf); 650 free (w->cbc.buf); 651 if (! w->finished) 652 externalErrorExitDesc ("The worker thread did't signal 'finished' state"); 653 ret += 2 - w->success; 654 } 655 656 MHD_stop_daemon (d); 657 return ret; 658 } 659 660 661 int 662 main (int argc, char *const *argv) 663 { 664 unsigned int errorCount = 0; 665 (void) argc; (void) argv; /* Unused. Silent compiler warning. */ 666 #if (LIBCURL_VERSION_MAJOR == 7) && (LIBCURL_VERSION_MINOR == 62) 667 if (1) 668 { 669 fprintf (stderr, "libcurl version 7.62.x has bug in processing " 670 "URI with GET arguments for Digest Auth.\n"); 671 fprintf (stderr, "This test cannot be performed.\n"); 672 exit (77); 673 } 674 #endif /* libcurl version 7.62.x */ 675 676 verbose = ! (has_param (argc, argv, "-q") || 677 has_param (argc, argv, "--quiet") || 678 has_param (argc, argv, "-s") || 679 has_param (argc, argv, "--silent")); 680 681 #ifdef NEED_GCRYP_INIT 682 gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0); 683 #ifdef GCRYCTL_INITIALIZATION_FINISHED 684 gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); 685 #endif /* GCRYCTL_INITIALIZATION_FINISHED */ 686 #endif /* NEED_GCRYP_INIT */ 687 if (0 != curl_global_init (CURL_GLOBAL_WIN32)) 688 return 2; 689 errorCount += testDigestAuth (); 690 if (errorCount != 0) 691 fprintf (stderr, "Error (code: %u)\n", errorCount); 692 curl_global_cleanup (); 693 return (0 == errorCount) ? 0 : 1; /* 0 == pass */ 694 }